killbill-aplcache
Changes
api/src/main/java/com/ning/billing/entitlement/api/repair/EntitlementRepairException.java 41(+41 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultBillingEvent.java 2(+1 -1)
entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java 10(+5 -5)
entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java 2(+1 -1)
entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java 6(+3 -3)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultDeletedEvent.java 42(+42 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java 341(+341 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultSubscriptionRepair.java 145(+145 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementLifecycleDao.java 28(+28 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionApiService.java 36(+36 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionFactory.java 56(+56 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionDataRepair.java 145(+145 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java 31(+6 -25)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java 29(+15 -14)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionFactory.java 14(+9 -5)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java 12(+9 -3)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java 5(+5 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionDataIterator.java 2(+1 -1)
entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java 230(+230 -0)
entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg 27(+20 -7)
entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultBillingEvent.java 2(+1 -1)
entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java 25(+13 -12)
entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java 12(+10 -2)
entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java 2(+1 -1)
invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java 2(+1 -1)
Details
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java b/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java
index 4f2e0d9..4c33dac 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java
@@ -23,8 +23,8 @@ import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.InternationalPrice;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
import java.math.BigDecimal;
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/repair/BundleRepair.java b/api/src/main/java/com/ning/billing/entitlement/api/repair/BundleRepair.java
new file mode 100644
index 0000000..27dadd6
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/repair/BundleRepair.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.List;
+import java.util.UUID;
+
+public interface BundleRepair {
+
+ String getViewId();
+
+ UUID getBundleId();
+
+ List<SubscriptionRepair> getSubscriptions();
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/repair/EntitlementRepairApi.java b/api/src/main/java/com/ning/billing/entitlement/api/repair/EntitlementRepairApi.java
new file mode 100644
index 0000000..de6ae59
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/repair/EntitlementRepairApi.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.UUID;
+
+import com.ning.billing.util.callcontext.CallContext;
+
+public interface EntitlementRepairApi {
+
+ public BundleRepair getBundleRepair(final UUID bundleId) throws EntitlementRepairException;
+
+ public BundleRepair repairBundle(final BundleRepair input, final boolean dryRun, final CallContext context) throws EntitlementRepairException;
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/repair/EntitlementRepairException.java b/api/src/main/java/com/ning/billing/entitlement/api/repair/EntitlementRepairException.java
new file mode 100644
index 0000000..e99a283
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/repair/EntitlementRepairException.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+
+public class EntitlementRepairException extends BillingExceptionBase {
+
+ private static final long serialVersionUID = 19067233L;
+
+ public EntitlementRepairException(EntitlementUserApiException e) {
+ super(e, e.getCode(), e.getMessage());
+ }
+
+ public EntitlementRepairException(CatalogApiException e) {
+ super(e, e.getCode(), e.getMessage());
+ }
+ public EntitlementRepairException(Throwable e, ErrorCode code, Object...args) {
+ super(e, code, args);
+ }
+
+ public EntitlementRepairException(ErrorCode code, Object...args) {
+ super(code, args);
+ }
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionRepair.java b/api/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionRepair.java
new file mode 100644
index 0000000..adab3b5
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionRepair.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+
+public interface SubscriptionRepair {
+
+ public UUID getId();
+
+ public List<DeletedEvent> getDeletedEvents();
+
+ public List<NewEvent> getNewEvents();
+
+ public List<ExistingEvent> getExistingEvents();
+
+ public interface DeletedEvent {
+ public UUID getEventId();
+ }
+
+ public interface NewEvent {
+ public PlanPhaseSpecifier getPlanPhaseSpecifier();
+ public DateTime getRequestedDate();
+ public SubscriptionTransitionType getSubscriptionTransitionType();
+
+ }
+
+ public interface ExistingEvent extends DeletedEvent, NewEvent {
+ public DateTime getEffectiveDate();
+ }
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/SubscriptionTransitionType.java b/api/src/main/java/com/ning/billing/entitlement/api/SubscriptionTransitionType.java
new file mode 100644
index 0000000..8e75675
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/SubscriptionTransitionType.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api;
+
+public enum SubscriptionTransitionType {
+ MIGRATE_ENTITLEMENT,
+ CREATE,
+ MIGRATE_BILLING,
+ CHANGE,
+ RE_CREATE,
+ CANCEL,
+ UNCANCEL,
+ PHASE
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApiException.java b/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApiException.java
index 693938b..08f2906 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApiException.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApiException.java
@@ -34,5 +34,4 @@ public class EntitlementUserApiException extends BillingExceptionBase {
public EntitlementUserApiException(ErrorCode code, Object...args) {
super(code, args);
}
-
}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionEventTransition.java b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionEventTransition.java
index b1efaad..7521f92 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionEventTransition.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionEventTransition.java
@@ -18,6 +18,7 @@ package com.ning.billing.entitlement.api.user;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
import com.ning.billing.util.bus.BusEvent;
import org.joda.time.DateTime;
@@ -26,17 +27,6 @@ import java.util.UUID;
public interface SubscriptionEventTransition extends BusEvent {
- public enum SubscriptionTransitionType {
- MIGRATE_ENTITLEMENT,
- CREATE,
- MIGRATE_BILLING,
- CHANGE,
- RE_CREATE,
- CANCEL,
- UNCANCEL,
- PHASE
- }
-
UUID getId();
SubscriptionTransitionType getTransitionType();
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index 57ea8d9..8a833ec 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -18,6 +18,8 @@ package com.ning.billing;
public enum ErrorCode {
+
+
/*
* Range 0 : COMMON EXCEPTIONS
*/
@@ -56,6 +58,19 @@ public enum ErrorCode {
ENT_GET_NO_BUNDLE_FOR_SUBSCRIPTION(1080, "Could not find a bundle for subscription %s"),
ENT_GET_INVALID_BUNDLE_ID(1081, "Could not find a bundle matching id %s"),
ENT_INVALID_SUBSCRIPTION_ID(1082, "Unknown subscription %s"),
+
+ /* Repair */
+ ENT_REPAIR_INVALID_DELETE_SET(1091, "Event %s is not deleted for subscription %s but prior events were"),
+ ENT_REPAIR_NON_EXISTENT_DELETE_EVENT(1092, "Event %s does not exist for subscription %s"),
+ ENT_REPAIR_MISSING_AO_DELETE_EVENT(1093, "Event %s should be in deleted set for subscription %s because BP events got deleted earlier"),
+ ENT_REPAIR_NEW_AO_EVENT_BEFORE_BP(1094, "New event %s for subscription %s is before last remaining event for BP"),
+ ENT_REPAIR_INVALID_NEW_AO_EVENT(1095, "New event %s for subscription %s is before last remaining event"),
+ ENT_REPAIR_UNKNOWN_TYPE(1096, "Unknown new event type %s for subscription %s"),
+ ENT_REPAIR_UNKNOWN_BUNDLE(1097, "Unknown bundle %s"),
+ ENT_REPAIR_UNKNOWN_SUBSCRIPTION(1098, "Unknown subscription %s"),
+ ENT_REPAIR_NO_ACTIVE_SUBSCRIPTIONS(1099, "No active subscriptions on bundle %s"),
+ ENT_REPAIR_VIEW_CHANGED(1100, "View for bundle %s has changed from %s to %s"),
+
/*
*
* Range 2000 : CATALOG
@@ -153,6 +168,7 @@ public enum ErrorCode {
INVOICE_TARGET_DATE_TOO_FAR_IN_THE_FUTURE(4005, "The target date was too far in the future. Target Date: %s")
;
+
private int code;
private String format;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultBillingEvent.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultBillingEvent.java
index bc89040..6acc98d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultBillingEvent.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultBillingEvent.java
@@ -23,9 +23,9 @@ import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
import java.math.BigDecimal;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
index 8c3e83f..a3a22cf 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
@@ -45,18 +45,17 @@ import com.ning.billing.catalog.api.BillingAlignment;
import com.ning.billing.catalog.api.Catalog;
import com.ning.billing.catalog.api.CatalogApiException;
import com.ning.billing.catalog.api.CatalogService;
-import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.catalog.api.Product;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
import com.ning.billing.entitlement.engine.dao.SubscriptionSqlDao;
@@ -72,7 +71,8 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
private static final String SUBSCRIPTION_TABLE_NAME = "subscriptions";
@Inject
- public DefaultEntitlementBillingApi(final CallContextFactory factory, final SubscriptionFactory subscriptionFactory, final EntitlementDao dao, final AccountUserApi accountApi, final CatalogService catalogService) {
+ public DefaultEntitlementBillingApi(final CallContextFactory factory, final SubscriptionFactory subscriptionFactory,
+ final EntitlementDao dao, final AccountUserApi accountApi, final CatalogService catalogService) {
super();
this.factory = factory;
this.subscriptionFactory = subscriptionFactory;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java
index 24c4e07..07a0034 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java
@@ -20,7 +20,7 @@ import java.util.List;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.events.EntitlementEvent;
import com.ning.billing.entitlement.events.user.ApiEventMigrateBilling;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
index 8390504..b09fc17 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
@@ -31,12 +31,12 @@ import com.google.inject.Inject;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.entitlement.alignment.MigrationPlanAligner;
import com.ning.billing.entitlement.alignment.TimedMigration;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
import com.ning.billing.entitlement.events.EntitlementEvent;
import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
@@ -86,7 +86,7 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
for (final EntitlementBundleMigration curBundle : toBeMigrated.getBundles()) {
- SubscriptionBundleData bundleData = new SubscriptionBundleData(curBundle.getBundleKey(), accountId);
+ SubscriptionBundleData bundleData = new SubscriptionBundleData(curBundle.getBundleKey(), accountId, clock.getUTCNow());
List<SubscriptionMigrationData> bundleSubscriptionData = new LinkedList<AccountMigrationData.SubscriptionMigrationData>();
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultDeletedEvent.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultDeletedEvent.java
new file mode 100644
index 0000000..0a74fda
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultDeletedEvent.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.DeletedEvent;
+
+public class DefaultDeletedEvent implements DeletedEvent {
+
+ private final UUID id;
+ private final DateTime effectiveDate;
+
+ public DefaultDeletedEvent(UUID id, DateTime effectiveDate) {
+ this.id = id;
+ this.effectiveDate = effectiveDate;
+ }
+
+ @Override
+ public UUID getEventId() {
+ return id;
+ }
+
+ public DateTime getEffectiveDate() {
+ return effectiveDate;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java
new file mode 100644
index 0000000..5dbb3a2
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.ExistingEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.NewEvent;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
+import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.util.callcontext.CallContext;
+
+public class DefaultEntitlementRepairApi implements EntitlementRepairApi {
+
+ private final EntitlementDao dao;
+ private final SubscriptionFactory factory;
+ private final RepairEntitlementLifecycleDao repairDao;
+
+ @Inject
+ public DefaultEntitlementRepairApi(@Named(EntitlementModule.REPAIR_NAMED) final SubscriptionFactory factory,
+ @Named(EntitlementModule.REPAIR_NAMED) final RepairEntitlementLifecycleDao repairDao, final EntitlementDao dao) {
+ this.dao = dao;
+ this.repairDao = repairDao;
+ this.factory = factory;
+ }
+
+
+ @Override
+ public BundleRepair getBundleRepair(final UUID bundleId)
+ throws EntitlementRepairException {
+
+ SubscriptionBundle bundle = dao.getSubscriptionBundleFromId(bundleId);
+ if (bundle == null) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_BUNDLE, bundleId);
+ }
+ final List<Subscription> subscriptions = dao.getSubscriptions(factory, bundleId);
+ if (subscriptions.size() == 0) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NO_ACTIVE_SUBSCRIPTIONS, bundleId);
+ }
+ final String viewId = getViewId(((SubscriptionBundleData) bundle).getLastSysUpdateTime(), subscriptions);
+ final List<SubscriptionRepair> repairs = createGetSubscriptionRepairList(subscriptions, Collections.<SubscriptionRepair>emptyList());
+ return createGetBundleRepair(bundleId, viewId, repairs);
+ }
+
+
+
+ @Override
+ public BundleRepair repairBundle(final BundleRepair input, final boolean dryRun, final CallContext context)
+ throws EntitlementRepairException {
+
+ try {
+
+ SubscriptionBundle bundle = dao.getSubscriptionBundleFromId(input.getBundleId());
+ if (bundle == null) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_BUNDLE, input.getBundleId());
+ }
+
+ // Subscriptions are ordered with BASE subscription first-- if exists
+ final List<Subscription> subscriptions = dao.getSubscriptions(factory, input.getBundleId());
+ if (subscriptions.size() == 0) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_BUNDLE,input.getBundleId());
+ }
+
+ final String viewId = getViewId(((SubscriptionBundleData) bundle).getLastSysUpdateTime(), subscriptions);
+ if (!viewId.equals(input.getViewId())) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_VIEW_CHANGED,input.getBundleId(), input.getViewId(), viewId);
+ }
+
+ DateTime firstDeletedBPEventTime = null;
+ DateTime lastRemainingBPEventTime = null;
+
+ SubscriptionDataRepair baseSubscriptionRepair = null;
+ List<SubscriptionDataRepair> addOnSubscriptionInRepair = new LinkedList<SubscriptionDataRepair>();
+ List<SubscriptionDataRepair> inRepair = new LinkedList<SubscriptionDataRepair>();
+ for (Subscription cur : subscriptions) {
+
+ SubscriptionRepair curRepair = findAndCreateSubscriptionRepair(cur.getId(), input.getSubscriptions());
+ if (curRepair != null) {
+ SubscriptionDataRepair curData = ((SubscriptionDataRepair) cur);
+ List<EntitlementEvent> remaining = getRemainingEventsAndValidateDeletedEvents(curData, firstDeletedBPEventTime, curRepair.getDeletedEvents());
+
+ if (cur.getCategory() == ProductCategory.BASE) {
+ int bpTransitionSize =((SubscriptionData) cur).getAllTransitions().size();
+ lastRemainingBPEventTime = (remaining.size() > 0) ? curData.getAllTransitions().get(remaining.size() - 1).getEffectiveTransitionTime() : null;
+ firstDeletedBPEventTime = (remaining.size() < bpTransitionSize) ? curData.getAllTransitions().get(remaining.size()).getEffectiveTransitionTime() : null;
+ }
+
+ if (curRepair.getNewEvents() != null && curRepair.getNewEvents().size() > 0) {
+ Collections.sort(curRepair.getNewEvents(), new Comparator<NewEvent>() {
+ @Override
+ public int compare(NewEvent o1, NewEvent o2) {
+ return o1.getRequestedDate().compareTo(o2.getRequestedDate());
+ }
+ });
+ DateTime lastRemainingEventTime = (remaining.size() == 0) ? null : curData.getAllTransitions().get(remaining.size() - 1).getEffectiveTransitionTime();
+ validateFirstNewEvent(curData, curRepair.getNewEvents().get(0), lastRemainingBPEventTime, lastRemainingEventTime);
+ }
+ SubscriptionDataRepair sRepair = createSubscriptionDataRepair(curData, remaining);
+ repairDao.initializeRepair(curData.getId(), remaining);
+ inRepair.add(sRepair);
+ if (sRepair.getCategory() == ProductCategory.ADD_ON) {
+ addOnSubscriptionInRepair.add(sRepair);
+ } else if (sRepair.getCategory() == ProductCategory.BASE) {
+ baseSubscriptionRepair = sRepair;
+ }
+ }
+ }
+
+ if (input.getSubscriptions().size() != inRepair.size()) {
+ for (SubscriptionRepair cur : input.getSubscriptions()) {
+ boolean found = false;
+ for (Subscription s : subscriptions) {
+ if (s.getId().equals(cur.getId())) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_SUBSCRIPTION, cur.getId());
+ }
+ }
+ }
+
+ TreeSet<NewEvent> newEventSet = new TreeSet<SubscriptionRepair.NewEvent>(new Comparator<NewEvent>() {
+ @Override
+ public int compare(NewEvent o1, NewEvent o2) {
+ return o1.getRequestedDate().compareTo(o2.getRequestedDate());
+ }
+ });
+ for (SubscriptionRepair cur : input.getSubscriptions()) {
+ for (NewEvent e : cur.getNewEvents()) {
+ newEventSet.add(new DefaultNewEvent(cur.getId(), e.getPlanPhaseSpecifier(), e.getRequestedDate(), e.getSubscriptionTransitionType()));
+ }
+ }
+
+ Iterator<NewEvent> it = newEventSet.iterator();
+ while (it.hasNext()) {
+ DefaultNewEvent cur = (DefaultNewEvent) it.next();
+ SubscriptionDataRepair curDataRepair = findSubscriptionDataRepair(cur.getSubscriptionId(), inRepair);
+ if (curDataRepair == null) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_SUBSCRIPTION, cur.getSubscriptionId());
+ }
+ curDataRepair.addNewRepairEvent(cur, baseSubscriptionRepair, addOnSubscriptionInRepair, context);
+ }
+
+ if (dryRun) {
+ final List<SubscriptionRepair> repairs = createGetSubscriptionRepairList(subscriptions, convertDataRepair(inRepair));
+ return createGetBundleRepair(input.getBundleId(), input.getViewId(), repairs);
+ } else {
+ // STEPH no implemented yet
+ return null;
+ }
+ } finally {
+ repairDao.cleanup();
+ }
+ }
+
+ private String getViewId(DateTime lastUpdateBundleDate, List<Subscription> subscriptions) {
+ StringBuilder tmp = new StringBuilder();
+ long lastOrderedId = -1;
+ for (Subscription cur : subscriptions) {
+ lastOrderedId = lastOrderedId < ((SubscriptionData) cur).getLastEventOrderedId() ? ((SubscriptionData) cur).getLastEventOrderedId() : lastOrderedId;
+ }
+ tmp.append(lastOrderedId);
+ tmp.append("-");
+ tmp.append(lastUpdateBundleDate.toDate().getTime());
+ return tmp.toString();
+ }
+
+ private BundleRepair createGetBundleRepair(final UUID bundleId, final String viewId, final List<SubscriptionRepair> repairList) {
+ return new BundleRepair() {
+ @Override
+ public String getViewId() {
+ return viewId;
+ }
+ @Override
+ public List<SubscriptionRepair> getSubscriptions() {
+ return repairList;
+ }
+ @Override
+ public UUID getBundleId() {
+ return bundleId;
+ }
+ };
+
+ }
+
+ private List<SubscriptionRepair> createGetSubscriptionRepairList(final List<Subscription> subscriptions, final List<SubscriptionRepair> inRepair) {
+
+ final List<SubscriptionRepair> result = new LinkedList<SubscriptionRepair>();
+ Set<UUID> repairIds = new TreeSet<UUID>();
+ for (final SubscriptionRepair cur : inRepair) {
+ repairIds.add(cur.getId());
+ result.add(cur);
+ }
+ for (final Subscription cur : subscriptions) {
+ if ( !repairIds.contains(cur.getId())) {
+ result.add(new DefaultSubscriptionRepair((SubscriptionData) cur));
+ }
+ }
+ return result;
+ }
+
+
+ private List<SubscriptionRepair> convertDataRepair(List<SubscriptionDataRepair> input) {
+ List<SubscriptionRepair> result = new LinkedList<SubscriptionRepair>();
+ for (SubscriptionDataRepair cur : input) {
+ result.add(new DefaultSubscriptionRepair(cur));
+ }
+ return result;
+ }
+
+ private SubscriptionDataRepair findSubscriptionDataRepair(final UUID targetId, final List<SubscriptionDataRepair> input) {
+ for (SubscriptionDataRepair cur : input) {
+ if (cur.getId().equals(targetId)) {
+ return cur;
+ }
+ }
+ return null;
+ }
+
+
+ private SubscriptionDataRepair createSubscriptionDataRepair(final SubscriptionData curData, final List<EntitlementEvent> initialEvents) {
+ SubscriptionBuilder builder = new SubscriptionBuilder(curData);
+ builder.setActiveVersion(curData.getActiveVersion() + 1);
+ SubscriptionDataRepair result = (SubscriptionDataRepair) factory.createSubscription(builder, initialEvents);
+ return result;
+ }
+
+ private void validateFirstNewEvent(final SubscriptionData data, final NewEvent firstNewEvent, final DateTime lastBPRemainingTime, final DateTime lastRemainingTime)
+ throws EntitlementRepairException {
+ if (lastBPRemainingTime != null &&
+ firstNewEvent.getRequestedDate().isBefore(lastBPRemainingTime)) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NEW_AO_EVENT_BEFORE_BP, firstNewEvent.getPlanPhaseSpecifier().toString(), data.getId());
+ }
+ if (lastRemainingTime != null &&
+ firstNewEvent.getRequestedDate().isBefore(lastRemainingTime)) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_INVALID_NEW_AO_EVENT, firstNewEvent.getPlanPhaseSpecifier().toString(), data.getId());
+ }
+
+ }
+
+ private List<EntitlementEvent> getRemainingEventsAndValidateDeletedEvents(final SubscriptionDataRepair data, final DateTime firstBPDeletedTime,
+ final List<SubscriptionRepair.DeletedEvent> deletedEvents)
+ throws EntitlementRepairException {
+
+ if (deletedEvents == null || deletedEvents.size() == 0) {
+ return data.getEvents();
+ }
+
+ int nbDeleted = 0;
+ LinkedList<EntitlementEvent> result = new LinkedList<EntitlementEvent>();
+ for (EntitlementEvent cur : data.getEvents()) {
+
+ boolean foundDeletedEvent = false;
+ for (SubscriptionRepair.DeletedEvent d : deletedEvents) {
+ if (cur.getId().equals(d.getEventId())) {
+ foundDeletedEvent = true;
+ nbDeleted++;
+ break;
+ }
+ }
+ if (!foundDeletedEvent && nbDeleted > 0) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_INVALID_DELETE_SET, cur.getId(), data.getId());
+ }
+ if (firstBPDeletedTime != null &&
+ ! cur.getEffectiveDate().isBefore(firstBPDeletedTime) &&
+ ! foundDeletedEvent) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_MISSING_AO_DELETE_EVENT, cur.getId(), data.getId());
+ }
+
+ if (nbDeleted == 0) {
+ result.add(cur);
+ }
+ }
+ if (nbDeleted != deletedEvents.size()) {
+ for (SubscriptionRepair.DeletedEvent d : deletedEvents) {
+ boolean found = false;
+ for (SubscriptionTransitionData cur : data.getAllTransitions()) {
+ if (cur.getId().equals(d.getEventId())) {
+ found = true;
+ continue;
+ }
+ }
+ if (!found) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NON_EXISTENT_DELETE_EVENT, d.getEventId(), data.getId());
+ }
+ }
+
+ }
+ return result;
+ }
+
+ private SubscriptionRepair findAndCreateSubscriptionRepair(final UUID target, final List<SubscriptionRepair> input) {
+ for (SubscriptionRepair cur : input) {
+ if (target.equals(cur.getId())) {
+ return new DefaultSubscriptionRepair(cur);
+ }
+ }
+ return null;
+ }
+}
+
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultNewEvent.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultNewEvent.java
new file mode 100644
index 0000000..8b2e3bf
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultNewEvent.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.NewEvent;
+
+public class DefaultNewEvent implements NewEvent {
+
+ private final UUID subscriptionId;
+ private final PlanPhaseSpecifier spec;
+ private final DateTime requestedDate;
+ private final SubscriptionTransitionType transitionType;
+
+ public DefaultNewEvent(final UUID subscriptionId, final PlanPhaseSpecifier spec, final DateTime requestedDate, final SubscriptionTransitionType transitionType) {
+ this.subscriptionId = subscriptionId;
+ this.spec = spec;
+ this.requestedDate = requestedDate;
+ this.transitionType = transitionType;
+ }
+
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+ return spec;
+ }
+
+ @Override
+ public DateTime getRequestedDate() {
+ return requestedDate;
+ }
+
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() {
+ return transitionType;
+ }
+
+ public UUID getSubscriptionId() {
+ return subscriptionId;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultSubscriptionRepair.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultSubscriptionRepair.java
new file mode 100644
index 0000000..6048ff1
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultSubscriptionRepair.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.ExistingEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.NewEvent;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
+
+public class DefaultSubscriptionRepair implements SubscriptionRepair {
+
+ private final UUID id;
+ private final List<ExistingEvent> existingEvents;
+ private final List<NewEvent> newEvents;
+ private final List<DeletedEvent> deletedEvents;
+
+
+ public DefaultSubscriptionRepair(SubscriptionRepair input) {
+ this.id = input.getId();
+ this.existingEvents = (input.getExistingEvents() != null) ? new ArrayList<SubscriptionRepair.ExistingEvent>(input.getExistingEvents()) : null;
+ sortExistingEvent(this.existingEvents);
+ this.deletedEvents = (input.getDeletedEvents() != null) ? new ArrayList<SubscriptionRepair.DeletedEvent>(input.getDeletedEvents()) : null;
+ this.newEvents = (input.getNewEvents() != null) ? new ArrayList<SubscriptionRepair.NewEvent>(input.getNewEvents()) : null;
+ sortNewEvent(this.newEvents);
+ }
+
+ // CTOR for returning events only
+ public DefaultSubscriptionRepair(SubscriptionData input) {
+ this.id = input.getId();
+ this.existingEvents = toExistingEvents(input.getCategory(), input.getAllTransitions());
+ this.deletedEvents = null;
+ this.newEvents = null;
+ }
+
+ private List<ExistingEvent> toExistingEvents(final ProductCategory category, final List<SubscriptionTransitionData> transitions) {
+ List<ExistingEvent> result = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ for (final SubscriptionTransitionData cur : transitions) {
+
+ String productName = null;
+ BillingPeriod billingPeriod = null;
+ String priceListName = null;
+ PhaseType phaseType = null;
+ if (cur.getTransitionType() != SubscriptionTransitionType.CANCEL) {
+ productName = cur.getNextPlan().getProduct().getName();
+ billingPeriod = cur.getNextPhase().getBillingPeriod();
+ priceListName = cur.getNextPriceList();
+ phaseType = cur.getNextPhase().getPhaseType();
+ }
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceListName, phaseType);
+ result.add(new ExistingEvent() {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() {
+ return cur.getTransitionType();
+ }
+ @Override
+ public DateTime getRequestedDate() {
+ return cur.getRequestedTransitionTime();
+ }
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+ return spec;
+ }
+ @Override
+ public UUID getEventId() {
+ return cur.getId();
+ }
+ @Override
+ public DateTime getEffectiveDate() {
+ return cur.getEffectiveTransitionTime();
+ }
+ });
+ }
+ sortExistingEvent(result);
+ return result;
+ }
+
+ @Override
+ public UUID getId() {
+ return id;
+ }
+
+ @Override
+ public List<DeletedEvent> getDeletedEvents() {
+ return deletedEvents;
+ }
+
+ @Override
+ public List<NewEvent> getNewEvents() {
+ return newEvents;
+ }
+
+ @Override
+ public List<ExistingEvent> getExistingEvents() {
+ return existingEvents;
+ }
+
+ private void sortExistingEvent(final List<ExistingEvent> events) {
+ if (events != null) {
+ Collections.sort(events, new Comparator<ExistingEvent>() {
+ @Override
+ public int compare(ExistingEvent arg0, ExistingEvent arg1) {
+ return arg0.getEffectiveDate().compareTo(arg1.getEffectiveDate());
+ }
+ });
+ }
+ }
+ private void sortNewEvent(final List<NewEvent> events) {
+ if (events != null) {
+ Collections.sort(events, new Comparator<NewEvent>() {
+ @Override
+ public int compare(NewEvent arg0, NewEvent arg1) {
+ return arg0.getRequestedDate().compareTo(arg1.getRequestedDate());
+ }
+ });
+ }
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementLifecycleDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementLifecycleDao.java
new file mode 100644
index 0000000..cf482e5
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementLifecycleDao.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.List;
+import java.util.UUID;
+
+import com.ning.billing.entitlement.events.EntitlementEvent;
+
+public interface RepairEntitlementLifecycleDao {
+
+ public void initializeRepair(final UUID subscriptionId, final List<EntitlementEvent> initialEvents);
+
+ public void cleanup();
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionApiService.java
new file mode 100644
index 0000000..dbcca32
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionApiService.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.entitlement.alignment.PlanAligner;
+import com.ning.billing.entitlement.api.SubscriptionApiService;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionApiService;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.util.clock.Clock;
+
+public class RepairSubscriptionApiService extends DefaultSubscriptionApiService implements SubscriptionApiService {
+
+ @Inject
+ public RepairSubscriptionApiService(Clock clock, @Named(EntitlementModule.REPAIR_NAMED) EntitlementDao dao,
+ CatalogService catalogService, PlanAligner planAligner) {
+ super(clock, dao, catalogService, planAligner);
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionFactory.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionFactory.java
new file mode 100644
index 0000000..eb32bb0
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionFactory.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.List;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.entitlement.api.SubscriptionApiService;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionApiService;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.engine.addon.AddonUtils;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.util.clock.Clock;
+
+public class RepairSubscriptionFactory extends DefaultSubscriptionFactory implements SubscriptionFactory {
+
+ private final AddonUtils addonUtils;
+
+ @Inject
+ public RepairSubscriptionFactory(@Named(EntitlementModule.REPAIR_NAMED) SubscriptionApiService apiService, Clock clock, CatalogService catalogService, AddonUtils addonUtils) {
+ super(apiService, clock, catalogService);
+ this.addonUtils = addonUtils;
+ }
+
+ @Override
+ public SubscriptionData createSubscription(SubscriptionBuilder builder,
+ List<EntitlementEvent> events) {
+ SubscriptionData subscription = new SubscriptionDataRepair(builder, apiService, clock, addonUtils);
+ if (events.size() > 0) {
+ for (EntitlementEvent cur : events) {
+ cur.setActiveVersion(builder.getActiveVersion());
+ }
+ subscription.rebuildTransitions(events, catalogService.getFullCatalog());
+ }
+ return subscription;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionDataRepair.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionDataRepair.java
new file mode 100644
index 0000000..7b1f02b
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionDataRepair.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.Catalog;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PlanSpecifier;
+import com.ning.billing.catalog.api.Product;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionApiService;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.engine.addon.AddonUtils;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.events.user.ApiEventBuilder;
+import com.ning.billing.entitlement.events.user.ApiEventCancel;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.clock.Clock;
+import com.sun.org.apache.xml.internal.resolver.CatalogException;
+
+public class SubscriptionDataRepair extends SubscriptionData {
+
+ private final AddonUtils addonUtils;
+ private final Clock clock;
+
+ private final List<EntitlementEvent> newEvents;
+
+
+ // Low level events are ONLY used for Repair APIs
+ protected List<EntitlementEvent> events;
+
+
+ public SubscriptionDataRepair(SubscriptionBuilder builder, SubscriptionApiService apiService,
+ Clock clock, AddonUtils addonUtils) {
+ super(builder, apiService, clock);
+ this.addonUtils = addonUtils;
+ this.clock = clock;
+ this.newEvents = new LinkedList<EntitlementEvent>();
+ }
+
+ public void addNewRepairEvent(final DefaultNewEvent input, final SubscriptionDataRepair baseSubscription, final List<SubscriptionDataRepair> addonSubscriptions, final CallContext context)
+ throws EntitlementRepairException {
+
+
+ try {
+ final PlanPhaseSpecifier spec = input.getPlanPhaseSpecifier();
+ switch(input.getSubscriptionTransitionType()) {
+ case CREATE:
+ case RE_CREATE:
+ recreate(spec, input.getRequestedDate(), context);
+ break;
+ case CHANGE:
+ changePlan(spec.getProductName(), spec.getBillingPeriod(), spec.getPriceListName(), input.getRequestedDate(), context);
+ break;
+ case CANCEL:
+ cancel(input.getRequestedDate(), false, context);
+ break;
+ case PHASE:
+ break;
+ default:
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_TYPE, input.getSubscriptionTransitionType(), id);
+ }
+
+ trickleDownBPEffectForAddon(addonSubscriptions, input.getRequestedDate(), context);
+ checkAddonRights(baseSubscription);
+
+ } catch (EntitlementUserApiException e) {
+ throw new EntitlementRepairException(e);
+ } catch (CatalogApiException e) {
+ throw new EntitlementRepairException(e);
+ }
+ }
+
+
+ private void trickleDownBPEffectForAddon(final List<SubscriptionDataRepair> addonSubscriptions, final DateTime requestedDate, final CallContext context)
+ throws EntitlementUserApiException {
+
+ if (category != ProductCategory.BASE) {
+ return;
+ }
+
+ DateTime now = clock.getUTCNow();
+ Product baseProduct = (getState() == SubscriptionState.CANCELLED ) ?
+ null : getCurrentPlan().getProduct();
+
+ Iterator<SubscriptionDataRepair> it = addonSubscriptions.iterator();
+ while (it.hasNext()) {
+ SubscriptionDataRepair cur = it.next();
+ if (cur.getState() == SubscriptionState.CANCELLED ||
+ cur.getCategory() != ProductCategory.ADD_ON) {
+ continue;
+ }
+ Plan addonCurrentPlan = cur.getCurrentPlan();
+ if (baseProduct == null ||
+ addonUtils.isAddonIncluded(baseProduct, addonCurrentPlan) ||
+ ! addonUtils.isAddonAvailable(baseProduct, addonCurrentPlan)) {
+
+ cur.cancel(requestedDate, false, context);
+ }
+ }
+ }
+
+ private void checkAddonRights(final SubscriptionDataRepair baseSubscription)
+ throws EntitlementUserApiException, CatalogApiException {
+ if (category == ProductCategory.ADD_ON) {
+ addonUtils.checkAddonCreationRights(baseSubscription, getCurrentPlan());
+ }
+ }
+
+ public void rebuildTransitions(final List<EntitlementEvent> inputEvents, final Catalog catalog) {
+ this.events = inputEvents;
+ super.rebuildTransitions(inputEvents, catalog);
+ }
+
+ public List<EntitlementEvent> getEvents() {
+ return events;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionApiService.java
new file mode 100644
index 0000000..046b982
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionApiService.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.util.callcontext.CallContext;
+
+public interface SubscriptionApiService {
+
+ public SubscriptionData createPlan(SubscriptionBuilder builder, Plan plan, PhaseType initialPhase,
+ String realPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate,
+ CallContext context)
+ throws EntitlementUserApiException;
+
+ public boolean recreatePlan(SubscriptionData subscription, PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
+ throws EntitlementUserApiException;
+
+
+ public boolean cancel(SubscriptionData subscription, DateTime requestedDate, boolean eot, CallContext context)
+ throws EntitlementUserApiException;
+
+ public boolean uncancel(SubscriptionData subscription, CallContext context)
+ throws EntitlementUserApiException;
+
+ public boolean changePlan(SubscriptionData subscription, String productName, BillingPeriod term,
+ String priceList, DateTime requestedDate, CallContext context)
+ throws EntitlementUserApiException;
+
+ public void commitCustomFields(SubscriptionData subscription, CallContext context);
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionFactory.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionFactory.java
new file mode 100644
index 0000000..2b12487
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionFactory.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api;
+
+import java.util.List;
+
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+
+public interface SubscriptionFactory {
+
+ public SubscriptionData createSubscription(SubscriptionBuilder builder, List<EntitlementEvent> events);
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
index 5554c9c..16f75d3 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
@@ -31,8 +31,9 @@ import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.catalog.api.PriceListSet;
import com.ning.billing.catalog.api.Product;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.engine.addon.AddonUtils;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
import com.ning.billing.entitlement.exceptions.EntitlementError;
@@ -43,13 +44,13 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
private final Clock clock;
private final EntitlementDao dao;
private final CatalogService catalogService;
- private final SubscriptionApiService apiService;
+ private final DefaultSubscriptionApiService apiService;
private final AddonUtils addonUtils;
private final SubscriptionFactory subscriptionFactory;
@Inject
public DefaultEntitlementUserApi(Clock clock, EntitlementDao dao, CatalogService catalogService,
- SubscriptionApiService apiService, final SubscriptionFactory subscriptionFactory, AddonUtils addonUtils) {
+ DefaultSubscriptionApiService apiService, final SubscriptionFactory subscriptionFactory, AddonUtils addonUtils) {
super();
this.clock = clock;
this.apiService = apiService;
@@ -92,7 +93,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
@Override
public SubscriptionBundle createBundleForAccount(UUID accountId, String bundleName, CallContext context)
throws EntitlementUserApiException {
- SubscriptionBundleData bundle = new SubscriptionBundleData(bundleName, accountId);
+ SubscriptionBundleData bundle = new SubscriptionBundleData(bundleName, accountId, clock.getUTCNow());
return dao.createSubscriptionBundle(bundle, context);
}
@@ -141,7 +142,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
if (baseSubscription == null) {
throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_NO_BP, bundleId);
}
- checkAddonCreationRights(baseSubscription, plan);
+ addonUtils.checkAddonCreationRights(baseSubscription, plan);
bundleStartDate = baseSubscription.getStartDate();
break;
case STANDALONE:
@@ -170,26 +171,6 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
}
}
-
- private void checkAddonCreationRights(SubscriptionData baseSubscription, Plan targetAddOnPlan)
- throws EntitlementUserApiException, CatalogApiException {
-
- if (baseSubscription.getState() != SubscriptionState.ACTIVE) {
- throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_BP_NON_ACTIVE, targetAddOnPlan.getName());
- }
-
- Product baseProduct = baseSubscription.getCurrentPlan().getProduct();
- if (addonUtils.isAddonIncluded(baseProduct, targetAddOnPlan)) {
- throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_ALREADY_INCLUDED,
- targetAddOnPlan.getName(), baseSubscription.getCurrentPlan().getProduct().getName());
- }
-
- if (!addonUtils.isAddonAvailable(baseProduct, targetAddOnPlan)) {
- throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_NOT_AVAILABLE,
- targetAddOnPlan.getName(), baseSubscription.getCurrentPlan().getProduct().getName());
- }
- }
-
@Override
public DateTime getNextBillingDate(UUID accountId) {
List<SubscriptionBundle> bundles = getBundlesForAccount(accountId);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java
index 8cc2573..75a4da1 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java
@@ -26,17 +26,19 @@ public class SubscriptionBundleData implements SubscriptionBundle {
private final String key;
private final UUID accountId;
private final DateTime startDate;
+ private final DateTime lastSysTimeUpdate;
- public SubscriptionBundleData(String name, UUID accountId) {
- this(UUID.randomUUID(), name, accountId, null);
+ public SubscriptionBundleData(String name, UUID accountId, DateTime now) {
+ this(UUID.randomUUID(), name, accountId, null, now);
}
- public SubscriptionBundleData(UUID id, String key, UUID accountId, DateTime startDate) {
+ public SubscriptionBundleData(UUID id, String key, UUID accountId, DateTime startDate, DateTime now) {
super();
this.id = id;
this.key = key;
this.accountId = accountId;
this.startDate = startDate;
+ this.lastSysTimeUpdate = now;
}
@Override
@@ -60,4 +62,8 @@ public class SubscriptionBundleData implements SubscriptionBundle {
public DateTime getStartDate() {
return startDate;
}
+
+ public DateTime getLastSysUpdateTime() {
+ return lastSysTimeUpdate;
+ }
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
index 103860f..701323e 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
@@ -24,8 +24,9 @@ import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.SubscriptionApiService;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.api.user.SubscriptionTransitionDataIterator.Kind;
import com.ning.billing.entitlement.api.user.SubscriptionTransitionDataIterator.Order;
import com.ning.billing.entitlement.api.user.SubscriptionTransitionDataIterator.TimeLimit;
@@ -51,41 +52,45 @@ import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
-public class SubscriptionData extends ExtendedEntityBase implements Subscription {
+public class SubscriptionData extends ExtendedEntityBase implements
+ Subscription {
private final static Logger log = LoggerFactory.getLogger(SubscriptionData.class);
- private final Clock clock;
- private final SubscriptionApiService apiService;
+
+ protected final Clock clock;
+ protected final SubscriptionApiService apiService;
//
// Final subscription fields
//
- private final UUID bundleId;
- private final DateTime startDate;
- private final DateTime bundleStartDate;
- private final ProductCategory category;
+ protected final UUID bundleId;
+ protected final DateTime startDate;
+ protected final DateTime bundleStartDate;
+ protected final ProductCategory category;
//
- // Those can be modified through non User APIs, and a new Subscription object would be created
+ // Those can be modified through non User APIs, and a new Subscription
+ // object would be created
//
- private final long activeVersion;
- private final DateTime chargedThroughDate;
- private final DateTime paidThroughDate;
+ protected final long activeVersion;
+ protected final DateTime chargedThroughDate;
+ protected final DateTime paidThroughDate;
+
//
// User APIs (create, change, cancel,...) will recompute those each time,
// so the user holding that subscription object get the correct state when
// the call completes
//
- private LinkedList<SubscriptionTransitionData> transitions;
+ protected LinkedList<SubscriptionTransitionData> transitions;
// Transient object never returned at the API
public SubscriptionData(SubscriptionBuilder builder) {
this(builder, null, null);
}
- public SubscriptionData(SubscriptionBuilder builder, @Nullable SubscriptionApiService apiService,
- @Nullable Clock clock) {
+ public SubscriptionData(SubscriptionBuilder builder,
+ @Nullable SubscriptionApiService apiService, @Nullable Clock clock) {
super(builder.getId(), null, null);
this.apiService = apiService;
this.clock = clock;
@@ -104,7 +109,8 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
}
@Override
- public void saveFieldValue(String fieldName, @Nullable String fieldValue, CallContext context) {
+ public void saveFieldValue(String fieldName, @Nullable String fieldValue,
+ CallContext context) {
super.setFieldValue(fieldName, fieldValue);
apiService.commitCustomFields(this, context);
}
@@ -133,26 +139,28 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
@Override
public SubscriptionState getState() {
- return (getPreviousTransition() == null) ? null : getPreviousTransition().getNextState();
+ return (getPreviousTransition() == null) ? null
+ : getPreviousTransition().getNextState();
}
@Override
public PlanPhase getCurrentPhase() {
- return (getPreviousTransition() == null) ? null : getPreviousTransition().getNextPhase();
+ return (getPreviousTransition() == null) ? null
+ : getPreviousTransition().getNextPhase();
}
-
@Override
public Plan getCurrentPlan() {
- return (getPreviousTransition() == null) ? null : getPreviousTransition().getNextPlan();
+ return (getPreviousTransition() == null) ? null
+ : getPreviousTransition().getNextPlan();
}
@Override
public String getCurrentPriceList() {
- return (getPreviousTransition() == null) ? null : getPreviousTransition().getNextPriceList();
+ return (getPreviousTransition() == null) ? null
+ : getPreviousTransition().getNextPriceList();
}
-
@Override
public DateTime getEndDate() {
SubscriptionEventTransition latestTransition = getPreviousTransition();
@@ -162,26 +170,29 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
return null;
}
-
@Override
- public boolean cancel(DateTime requestedDate, boolean eot, CallContext context) throws EntitlementUserApiException {
+ public boolean cancel(DateTime requestedDate, boolean eot,
+ CallContext context) throws EntitlementUserApiException {
return apiService.cancel(this, requestedDate, eot, context);
}
@Override
- public boolean uncancel(CallContext context) throws EntitlementUserApiException {
+ public boolean uncancel(CallContext context)
+ throws EntitlementUserApiException {
return apiService.uncancel(this, context);
}
@Override
public boolean changePlan(String productName, BillingPeriod term,
- String priceList, DateTime requestedDate, CallContext context) throws EntitlementUserApiException {
- return apiService.changePlan(this, productName, term, priceList, requestedDate, context);
+ String priceList, DateTime requestedDate, CallContext context)
+ throws EntitlementUserApiException {
+ return apiService.changePlan(this, productName, term, priceList,
+ requestedDate, context);
}
@Override
- public boolean recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
- throws EntitlementUserApiException {
+ public boolean recreate(PlanPhaseSpecifier spec, DateTime requestedDate,
+ CallContext context) throws EntitlementUserApiException {
return apiService.recreatePlan(this, spec, requestedDate, context);
}
@@ -191,8 +202,9 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
return Collections.emptyList();
}
List<SubscriptionEventTransition> result = new ArrayList<SubscriptionEventTransition>();
- SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock, transitions,
- Order.ASC_FROM_PAST, Kind.BILLING, Visibility.ALL, TimeLimit.ALL);
+ SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
+ clock, transitions, Order.ASC_FROM_PAST, Kind.BILLING,
+ Visibility.ALL, TimeLimit.ALL);
while (it.hasNext()) {
result.add(it.next());
}
@@ -205,8 +217,9 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
if (transitions == null) {
return null;
}
- SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock, transitions,
- Order.ASC_FROM_PAST, Kind.ENTITLEMENT, Visibility.ALL, TimeLimit.FUTURE_ONLY);
+ SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
+ clock, transitions, Order.ASC_FROM_PAST, Kind.ENTITLEMENT,
+ Visibility.ALL, TimeLimit.FUTURE_ONLY);
return it.hasNext() ? it.next() : null;
}
@@ -215,23 +228,31 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
if (transitions == null) {
return null;
}
- SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock, transitions,
- Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT, Visibility.FROM_DISK_ONLY, TimeLimit.PAST_OR_PRESENT_ONLY);
+ SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
+ clock, transitions, Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT,
+ Visibility.FROM_DISK_ONLY, TimeLimit.PAST_OR_PRESENT_ONLY);
return it.hasNext() ? it.next() : null;
}
- public SubscriptionEventTransition getTransitionFromEvent(final EntitlementEvent event, final int seqId) {
+ public SubscriptionEventTransition getTransitionFromEvent(
+ final EntitlementEvent event, final int seqId) {
if (transitions == null || event == null) {
return null;
}
for (SubscriptionEventTransition cur : transitions) {
if (cur.getId().equals(event.getId())) {
- return new SubscriptionTransitionData((SubscriptionTransitionData) cur, seqId);
+ return new SubscriptionTransitionData(
+ (SubscriptionTransitionData) cur, seqId);
}
}
return null;
}
+ public long getLastEventOrderedId() {
+ return (getPreviousTransition() == null) ? null
+ : ((SubscriptionTransitionData) getPreviousTransition()).getTotalOrdering();
+ }
+
public long getActiveVersion() {
return activeVersion;
}
@@ -255,32 +276,40 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
return paidThroughDate;
}
+ public List<SubscriptionTransitionData> getAllTransitions() {
+ return transitions;
+ }
+
public SubscriptionTransitionData getInitialTransitionForCurrentPlan() {
if (transitions == null) {
- throw new EntitlementError(String.format("No transitions for subscription %s", getId()));
+ throw new EntitlementError(String.format(
+ "No transitions for subscription %s", getId()));
}
-
- SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock, transitions,
- Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT, Visibility.ALL, TimeLimit.PAST_OR_PRESENT_ONLY);
+ SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
+ clock, transitions, Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT,
+ Visibility.ALL, TimeLimit.PAST_OR_PRESENT_ONLY);
while (it.hasNext()) {
SubscriptionTransitionData cur = it.next();
- if (cur.getTransitionType() == SubscriptionTransitionType.CREATE ||
- cur.getTransitionType() == SubscriptionTransitionType.RE_CREATE ||
- cur.getTransitionType() == SubscriptionTransitionType.CHANGE ||
- cur.getTransitionType() == SubscriptionTransitionType.MIGRATE_ENTITLEMENT) {
+ if (cur.getTransitionType() == SubscriptionTransitionType.CREATE
+ || cur.getTransitionType() == SubscriptionTransitionType.RE_CREATE
+ || cur.getTransitionType() == SubscriptionTransitionType.CHANGE
+ || cur.getTransitionType() == SubscriptionTransitionType.MIGRATE_ENTITLEMENT) {
return cur;
}
}
- throw new EntitlementError(String.format("Failed to find InitialTransitionForCurrentPlan id = %s", getId().toString()));
+ throw new EntitlementError(String.format(
+ "Failed to find InitialTransitionForCurrentPlan id = %s",
+ getId().toString()));
}
public boolean isSubscriptionFutureCancelled() {
if (transitions == null) {
return false;
}
- SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock, transitions,
- Order.ASC_FROM_PAST, Kind.ENTITLEMENT, Visibility.ALL, TimeLimit.FUTURE_ONLY);
+ SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
+ clock, transitions, Order.ASC_FROM_PAST, Kind.ENTITLEMENT,
+ Visibility.ALL, TimeLimit.FUTURE_ONLY);
while (it.hasNext()) {
SubscriptionTransitionData cur = it.next();
if (cur.getTransitionType() == SubscriptionTransitionType.CANCEL) {
@@ -290,46 +319,53 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
return false;
}
- public DateTime getPlanChangeEffectiveDate(ActionPolicy policy, DateTime requestedDate) {
+ public DateTime getPlanChangeEffectiveDate(ActionPolicy policy,
+ DateTime requestedDate) {
if (policy == ActionPolicy.IMMEDIATE) {
return requestedDate;
}
if (policy != ActionPolicy.END_OF_TERM) {
- throw new EntitlementError(String.format("Unexpected policy type %s", policy.toString()));
+ throw new EntitlementError(String.format(
+ "Unexpected policy type %s", policy.toString()));
}
if (chargedThroughDate == null) {
return requestedDate;
} else {
- return chargedThroughDate.isBefore(requestedDate) ? requestedDate : chargedThroughDate;
+ return chargedThroughDate.isBefore(requestedDate) ? requestedDate
+ : chargedThroughDate;
}
}
public DateTime getCurrentPhaseStart() {
if (transitions == null) {
- throw new EntitlementError(String.format("No transitions for subscription %s", getId()));
+ throw new EntitlementError(String.format(
+ "No transitions for subscription %s", getId()));
}
- SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock, transitions,
- Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT, Visibility.ALL, TimeLimit.PAST_OR_PRESENT_ONLY);
+ SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
+ clock, transitions, Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT,
+ Visibility.ALL, TimeLimit.PAST_OR_PRESENT_ONLY);
while (it.hasNext()) {
SubscriptionTransitionData cur = it.next();
- if (cur.getTransitionType() == SubscriptionTransitionType.PHASE ||
- cur.getTransitionType() == SubscriptionTransitionType.CREATE ||
- cur.getTransitionType() == SubscriptionTransitionType.RE_CREATE ||
- cur.getTransitionType() == SubscriptionTransitionType.CHANGE ||
- cur.getTransitionType() == SubscriptionTransitionType.MIGRATE_ENTITLEMENT) {
+ if (cur.getTransitionType() == SubscriptionTransitionType.PHASE
+ || cur.getTransitionType() == SubscriptionTransitionType.CREATE
+ || cur.getTransitionType() == SubscriptionTransitionType.RE_CREATE
+ || cur.getTransitionType() == SubscriptionTransitionType.CHANGE
+ || cur.getTransitionType() == SubscriptionTransitionType.MIGRATE_ENTITLEMENT) {
return cur.getEffectiveTransitionTime();
}
}
- throw new EntitlementError(String.format("Failed to find CurrentPhaseStart id = %s", getId().toString()));
+ throw new EntitlementError(String.format(
+ "Failed to find CurrentPhaseStart id = %s", getId().toString()));
}
- public void rebuildTransitions(final List<EntitlementEvent> events, final Catalog catalog) {
+ public void rebuildTransitions(final List<EntitlementEvent> inputEvents,
+ final Catalog catalog) {
- if (events == null) {
+ if (inputEvents == null) {
return;
}
@@ -338,7 +374,7 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
String nextPhaseName = null;
String nextPriceList = null;
UUID nextUserToken = null;
-
+
SubscriptionState previousState = null;
String previousPriceList = null;
@@ -346,7 +382,7 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
Plan previousPlan = null;
PlanPhase previousPhase = null;
- for (final EntitlementEvent cur : events) {
+ for (final EntitlementEvent cur : inputEvents) {
if (!cur.isActive() || cur.getActiveVersion() < activeVersion) {
continue;
@@ -368,8 +404,8 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
apiEventType = userEV.getEventType();
isFromDisk = userEV.isFromDisk();
nextUserToken = userEV.getUserToken();
-
- switch(apiEventType) {
+
+ switch (apiEventType) {
case MIGRATE_BILLING:
case MIGRATE_ENTITLEMENT:
case CREATE:
@@ -396,43 +432,37 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
case UNCANCEL:
break;
default:
- throw new EntitlementError(String.format("Unexpected UserEvent type = %s",
- userEV.getEventType().toString()));
+ throw new EntitlementError(String.format(
+ "Unexpected UserEvent type = %s", userEV
+ .getEventType().toString()));
}
break;
default:
- throw new EntitlementError(String.format("Unexpected Event type = %s",
- cur.getType()));
+ throw new EntitlementError(String.format(
+ "Unexpected Event type = %s", cur.getType()));
}
-
Plan nextPlan = null;
PlanPhase nextPhase = null;
try {
- nextPlan = (nextPlanName != null) ? catalog.findPlan(nextPlanName, cur.getRequestedDate(), getStartDate()) : null;
- nextPhase = (nextPhaseName != null) ? catalog.findPhase(nextPhaseName, cur.getRequestedDate(), getStartDate()) : null;
+ nextPlan = (nextPlanName != null) ? catalog.findPlan(
+ nextPlanName, cur.getRequestedDate(), getStartDate())
+ : null;
+ nextPhase = (nextPhaseName != null) ? catalog.findPhase(
+ nextPhaseName, cur.getRequestedDate(), getStartDate())
+ : null;
} catch (CatalogApiException e) {
- log.error(String.format("Failed to build transition for subscription %s", id), e);
+ log.error(String.format(
+ "Failed to build transition for subscription %s", id),
+ e);
}
- SubscriptionTransitionData transition =
- new SubscriptionTransitionData(cur.getId(),
- id,
- bundleId,
- cur.getType(),
- apiEventType,
- cur.getRequestedDate(),
- cur.getEffectiveDate(),
- previousState,
- previousPlan,
- previousPhase,
- previousPriceList,
- nextState,
- nextPlan,
- nextPhase,
- nextPriceList,
- cur.getTotalOrdering(),
- nextUserToken,
- isFromDisk);
+ SubscriptionTransitionData transition = new SubscriptionTransitionData(
+ cur.getId(), id, bundleId, cur.getType(), apiEventType,
+ cur.getRequestedDate(), cur.getEffectiveDate(),
+ previousState, previousPlan, previousPhase,
+ previousPriceList, nextState, nextPlan, nextPhase,
+ nextPriceList, cur.getTotalOrdering(), nextUserToken,
+ isFromDisk);
transitions.add(transition);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java
index 80990cb..6b28140 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java
@@ -18,6 +18,7 @@ package com.ning.billing.entitlement.api.user;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
import com.ning.billing.entitlement.events.user.ApiEventType;
@@ -177,6 +178,10 @@ public class SubscriptionTransitionData implements SubscriptionEventTransition {
@Override
public SubscriptionTransitionType getTransitionType() {
+ return toSubscriptionTransitionType(eventType, apiEventType);
+ }
+
+ public static SubscriptionTransitionType toSubscriptionTransitionType(EventType eventType, ApiEventType apiEventType) {
switch(eventType) {
case API_USER:
return apiEventType.getSubscriptionTransitionType();
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionDataIterator.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionDataIterator.java
index 3e5e961..355628d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionDataIterator.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionDataIterator.java
@@ -19,7 +19,7 @@ package com.ning.billing.entitlement.api.user;
import java.util.Iterator;
import java.util.LinkedList;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.exceptions.EntitlementError;
import com.ning.billing.util.clock.Clock;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java
index e7c5139..48a3c99 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java
@@ -28,6 +28,7 @@ import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.Product;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
import com.ning.billing.entitlement.api.user.SubscriptionData;
import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
import com.ning.billing.entitlement.exceptions.EntitlementError;
@@ -43,6 +44,24 @@ public class AddonUtils {
this.catalogService = catalogService;
}
+ public void checkAddonCreationRights(SubscriptionData baseSubscription, Plan targetAddOnPlan)
+ throws EntitlementUserApiException, CatalogApiException {
+
+ if (baseSubscription.getState() != SubscriptionState.ACTIVE) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_BP_NON_ACTIVE, targetAddOnPlan.getName());
+ }
+
+ Product baseProduct = baseSubscription.getCurrentPlan().getProduct();
+ if (isAddonIncluded(baseProduct, targetAddOnPlan)) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_ALREADY_INCLUDED,
+ targetAddOnPlan.getName(), baseSubscription.getCurrentPlan().getProduct().getName());
+ }
+
+ if (!isAddonAvailable(baseProduct, targetAddOnPlan)) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_NOT_AVAILABLE,
+ targetAddOnPlan.getName(), baseSubscription.getCurrentPlan().getProduct().getName());
+ }
+ }
public boolean isAddonAvailable(final String basePlanName, final DateTime requestedDate, final Plan targetAddOnPlan) {
try {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
index 2984ac3..f22b2a3 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
@@ -36,6 +36,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
+import com.google.inject.name.Named;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.Product;
@@ -45,6 +46,7 @@ import com.ning.billing.config.NotificationConfig;
import com.ning.billing.entitlement.alignment.PlanAligner;
import com.ning.billing.entitlement.alignment.TimedPhase;
import com.ning.billing.entitlement.api.EntitlementService;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.billing.DefaultEntitlementBillingApi;
import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
import com.ning.billing.entitlement.api.migration.DefaultEntitlementMigrationApi;
@@ -52,7 +54,7 @@ import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory;
import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
import com.ning.billing.entitlement.api.user.SubscriptionData;
import com.ning.billing.entitlement.engine.addon.AddonUtils;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
index bccd559..29cc30e 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
@@ -38,6 +38,7 @@ import org.skife.jdbi.v2.tweak.ResultSetMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.util.Date;
import java.util.List;
import java.util.UUID;
@@ -48,13 +49,16 @@ public interface BundleSqlDao extends Transactional<BundleSqlDao>, CloseMe, Tran
public void insertBundle(@Bind(binder = SubscriptionBundleBinder.class) SubscriptionBundleData bundle,
@CallContextBinder final CallContext context);
+ @SqlUpdate
+ public void updateBundleLastSysTime(@Bind("id") String id, @Bind("last_sys_update_dt") Date lastSysUpdate);
+
@SqlQuery
@Mapper(ISubscriptionBundleSqlMapper.class)
public SubscriptionBundle getBundleFromId(@Bind("id") String id);
@SqlQuery
@Mapper(ISubscriptionBundleSqlMapper.class)
- public SubscriptionBundle getBundleFromKey(@Bind("name") String name);
+ public SubscriptionBundle getBundleFromKey(@Bind("external_key") String externalKey);
@SqlQuery
@Mapper(ISubscriptionBundleSqlMapper.class)
@@ -65,8 +69,9 @@ public interface BundleSqlDao extends Transactional<BundleSqlDao>, CloseMe, Tran
public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, SubscriptionBundleData bundle) {
stmt.bind("id", bundle.getId().toString());
stmt.bind("start_dt", getDate(bundle.getStartDate()));
- stmt.bind("name", bundle.getKey());
+ stmt.bind("external_key", bundle.getKey());
stmt.bind("account_id", bundle.getAccountId().toString());
+ stmt.bind("last_sys_update_dt", getDate(bundle.getLastSysUpdateTime()));
}
}
@@ -76,10 +81,11 @@ public interface BundleSqlDao extends Transactional<BundleSqlDao>, CloseMe, Tran
StatementContext ctx) throws SQLException {
UUID id = UUID.fromString(r.getString("id"));
- String name = r.getString("name");
+ String key = r.getString("external_key");
UUID accountId = UUID.fromString(r.getString("account_id"));
DateTime startDate = getDate(r, "start_dt");
- SubscriptionBundleData bundle = new SubscriptionBundleData(id, name, accountId, startDate);
+ DateTime lastSysUpdateDate = getDate(r, "last_sys_update_dt");
+ SubscriptionBundleData bundle = new SubscriptionBundleData(id, key, accountId, startDate, lastSysUpdateDate);
return bundle;
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
index 4415c1d..2373588 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
@@ -17,16 +17,17 @@
package com.ning.billing.entitlement.engine.dao;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.migration.AccountMigrationData;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
import com.ning.billing.entitlement.events.EntitlementEvent;
public interface EntitlementDao {
@@ -59,6 +60,8 @@ public interface EntitlementDao {
public EntitlementEvent getEventById(final UUID eventId);
+ public Map<UUID, List<EntitlementEvent>> getEventsForBundle(final UUID bundleId);
+
public List<EntitlementEvent> getEventsForSubscription(final UUID subscriptionId);
public List<EntitlementEvent> getPendingEventsForSubscription(final UUID subscriptionId);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
index 55238f5..fbb2cba 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
@@ -21,7 +21,10 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import com.ning.billing.util.ChangeType;
@@ -42,6 +45,7 @@ import com.google.inject.Inject;
import com.ning.billing.ErrorCode;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.migration.AccountMigrationData;
import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
@@ -49,8 +53,7 @@ import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.engine.addon.AddonUtils;
import com.ning.billing.entitlement.engine.core.Engine;
import com.ning.billing.entitlement.engine.core.EntitlementNotificationKey;
@@ -197,6 +200,8 @@ public class EntitlementSqlDao implements EntitlementDao {
TransactionStatus status) throws Exception {
transactionalDao.updateSubscription(subscription.getId().toString(), subscription.getActiveVersion(), ctd, ptd, context);
+ BundleSqlDao tmpDao = transactionalDao.become(BundleSqlDao.class);
+ tmpDao.updateBundleLastSysTime(subscription.getBundleId().toString(), clock.getUTCNow().toDate());
AuditSqlDao auditSqlDao = transactionalDao.become(AuditSqlDao.class);
String subscriptionId = subscription.getId().toString();
auditSqlDao.insertAuditFromTransaction(SUBSCRIPTIONS_TABLE_NAME, subscriptionId, ChangeType.UPDATE, context);
@@ -235,6 +240,29 @@ public class EntitlementSqlDao implements EntitlementDao {
}
@Override
+ public Map<UUID, List<EntitlementEvent>> getEventsForBundle(final UUID bundleId) {
+
+ Map<UUID, List<EntitlementEvent>> result = subscriptionsDao.inTransaction(new Transaction<Map<UUID, List<EntitlementEvent>>, SubscriptionSqlDao>() {
+ @Override
+ public Map<UUID, List<EntitlementEvent>> inTransaction(SubscriptionSqlDao transactional,
+ TransactionStatus status) throws Exception {
+ List<Subscription> subscriptions = transactional.getSubscriptionsFromBundleId(bundleId.toString());
+ if (subscriptions.size() == 0) {
+ return Collections.emptyMap();
+ }
+ EventSqlDao eventsDaoFromSameTransaction = transactional.become(EventSqlDao.class);
+ Map<UUID, List<EntitlementEvent>> result = new HashMap<UUID, List<EntitlementEvent>>();
+ for (Subscription cur : subscriptions) {
+ List<EntitlementEvent> events = eventsDaoFromSameTransaction.getEventsForSubscription(cur.getId().toString());
+ result.put(cur.getId(), events);
+ }
+ return result;
+ }
+ });
+ return result;
+ }
+
+ @Override
public List<EntitlementEvent> getPendingEventsForSubscription(UUID subscriptionId) {
Date now = clock.getUTCNow().toDate();
return eventsDao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
new file mode 100644
index 0000000..85247ec
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.engine.dao;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import com.ning.billing.entitlement.api.SubscriptionFactory;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementLifecycleDao;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.util.callcontext.CallContext;
+
+public class RepairEntitlementDao implements EntitlementDao, RepairEntitlementLifecycleDao {
+
+ private final ThreadLocal<Map<UUID, SubscriptionRepairEvent>> preThreadsInRepairSubscriptions = new ThreadLocal<Map<UUID, SubscriptionRepairEvent>>();
+
+ private final static class SubscriptionRepairEvent {
+
+ private final List<EntitlementEvent> events;
+
+ public SubscriptionRepairEvent(List<EntitlementEvent> initialEvents) {
+ events = new LinkedList<EntitlementEvent>();
+ if (initialEvents != null) {
+ events.addAll(initialEvents);
+ }
+ }
+ public List<EntitlementEvent> getEvents() {
+ Collections.sort(events, new Comparator<EntitlementEvent>() {
+ @Override
+ public int compare(EntitlementEvent o1, EntitlementEvent o2) {
+ return o1.compareTo(o2);
+ }
+ });
+ return events;
+ }
+ public void addEvents(List<EntitlementEvent> newEvents) {
+ events.addAll(newEvents);
+ }
+ }
+
+ private Map<UUID, SubscriptionRepairEvent> getRepairMap() {
+ if (preThreadsInRepairSubscriptions.get() == null) {
+ preThreadsInRepairSubscriptions.set(new HashMap<UUID, SubscriptionRepairEvent>());
+ }
+ return preThreadsInRepairSubscriptions.get();
+ }
+
+ private SubscriptionRepairEvent getRepairSubscriptionEvents(UUID subscriptionId) {
+ Map<UUID, SubscriptionRepairEvent> map = getRepairMap();
+ return map.get(subscriptionId);
+ }
+
+ @Override
+ public List<EntitlementEvent> getEventsForSubscription(UUID subscriptionId) {
+ SubscriptionRepairEvent target = getRepairSubscriptionEvents(subscriptionId);
+ return target.getEvents();
+ }
+
+ @Override
+ public void createSubscription(SubscriptionData subscription,
+ List<EntitlementEvent> createEvents, CallContext context) {
+ addEvents(subscription.getId(), createEvents);
+ }
+
+ @Override
+ public void recreateSubscription(UUID subscriptionId,
+ List<EntitlementEvent> recreateEvents, CallContext context) {
+ addEvents(subscriptionId, recreateEvents);
+ }
+
+ @Override
+ public void cancelSubscription(UUID subscriptionId,
+ EntitlementEvent cancelEvent, CallContext context, int cancelSeq) {
+ addEvents(subscriptionId, Collections.singletonList(cancelEvent));
+ }
+
+
+ @Override
+ public void changePlan(UUID subscriptionId,
+ List<EntitlementEvent> changeEvents, CallContext context) {
+ addEvents(subscriptionId, changeEvents);
+ }
+
+ @Override
+ public void initializeRepair(UUID subscriptionId, List<EntitlementEvent> initialEvents) {
+ Map<UUID, SubscriptionRepairEvent> map = getRepairMap();
+ if (map.get(subscriptionId) == null) {
+ SubscriptionRepairEvent value = new SubscriptionRepairEvent(initialEvents);
+ map.put(subscriptionId, value);
+ } else {
+ throw new EntitlementError(String.format("Unexpected SubscriptionRepairEvent %s for thread %s", subscriptionId, Thread.currentThread().getName()));
+ }
+ }
+
+ @Override
+ public void cleanup() {
+ Map<UUID, SubscriptionRepairEvent> map = getRepairMap();
+ map.clear();
+ }
+
+
+ private void addEvents(UUID subscriptionId, List<EntitlementEvent> events) {
+ SubscriptionRepairEvent target = getRepairSubscriptionEvents(subscriptionId);
+ target.addEvents(events);
+ }
+
+
+ @Override
+ public void uncancelSubscription(UUID subscriptionId,
+ List<EntitlementEvent> uncancelEvents, CallContext context) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public List<SubscriptionBundle> getSubscriptionBundleForAccount(UUID accountId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public SubscriptionBundle getSubscriptionBundleFromKey(String bundleKey) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public SubscriptionBundle getSubscriptionBundleFromId(UUID bundleId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public SubscriptionBundle createSubscriptionBundle(
+ SubscriptionBundleData bundle, CallContext context) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public Subscription getSubscriptionFromId(SubscriptionFactory factory,
+ UUID subscriptionId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public UUID getAccountIdFromSubscriptionId(UUID subscriptionId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public Subscription getBaseSubscription(SubscriptionFactory factory,
+ UUID bundleId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public List<Subscription> getSubscriptions(SubscriptionFactory factory,
+ UUID bundleId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public List<Subscription> getSubscriptionsForKey(
+ SubscriptionFactory factory, String bundleKey) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public void updateSubscription(SubscriptionData subscription,
+ CallContext context) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public void createNextPhaseEvent(UUID subscriptionId,
+ EntitlementEvent nextPhase, CallContext context) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public EntitlementEvent getEventById(UUID eventId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public Map<UUID, List<EntitlementEvent>> getEventsForBundle(UUID bundleId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+
+ @Override
+ public List<EntitlementEvent> getPendingEventsForSubscription(
+ UUID subscriptionId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+
+ @Override
+ public void migrate(UUID accountId, AccountMigrationData data,
+ CallContext context) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public void saveCustomFields(SubscriptionData subscription,
+ CallContext context) {
+ throw new EntitlementError("Not implemented");
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
index 04f2965..d50572e 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
@@ -19,7 +19,7 @@ package com.ning.billing.entitlement.engine.dao;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.CallContextBinder;
import com.ning.billing.util.dao.BinderBase;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
index 27d5b61..d1eae92 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
@@ -16,7 +16,7 @@
package com.ning.billing.entitlement.events.user;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
public enum ApiEventType {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java b/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
index e167807..a79705a 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
@@ -19,24 +19,36 @@ package com.ning.billing.entitlement.glue;
import org.skife.config.ConfigurationObjectFactory;
import com.google.inject.AbstractModule;
+import com.google.inject.name.Names;
import com.ning.billing.config.EntitlementConfig;
import com.ning.billing.entitlement.alignment.MigrationPlanAligner;
import com.ning.billing.entitlement.alignment.PlanAligner;
import com.ning.billing.entitlement.api.EntitlementService;
+import com.ning.billing.entitlement.api.SubscriptionApiService;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.billing.DefaultEntitlementBillingApi;
import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
import com.ning.billing.entitlement.api.migration.DefaultEntitlementMigrationApi;
import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
+import com.ning.billing.entitlement.api.repair.DefaultEntitlementRepairApi;
+import com.ning.billing.entitlement.api.repair.EntitlementRepairApi;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementLifecycleDao;
+import com.ning.billing.entitlement.api.repair.RepairSubscriptionApiService;
+import com.ning.billing.entitlement.api.repair.RepairSubscriptionFactory;
import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
-import com.ning.billing.entitlement.api.user.SubscriptionApiService;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionApiService;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory;
import com.ning.billing.entitlement.engine.addon.AddonUtils;
import com.ning.billing.entitlement.engine.core.Engine;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
import com.ning.billing.entitlement.engine.dao.EntitlementSqlDao;
+import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
public class EntitlementModule extends AbstractModule {
+
+ public static final String REPAIR_NAMED = "repair";
+
protected void installConfig() {
final EntitlementConfig config = new ConfigurationObjectFactory(System.getProperties()).build(EntitlementConfig.class);
bind(EntitlementConfig.class).toInstance(config);
@@ -44,16 +56,26 @@ public class EntitlementModule extends AbstractModule {
protected void installEntitlementDao() {
bind(EntitlementDao.class).to(EntitlementSqlDao.class).asEagerSingleton();
+ bind(EntitlementDao.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementLifecycleDao.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementDao.class).asEagerSingleton();
}
protected void installEntitlementCore() {
- bind(SubscriptionFactory.class).asEagerSingleton();
- bind(SubscriptionApiService.class).asEagerSingleton();
+
+ bind(SubscriptionFactory.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairSubscriptionFactory.class).asEagerSingleton();
+ bind(SubscriptionFactory.class).to(DefaultSubscriptionFactory.class).asEagerSingleton();
+
+ bind(SubscriptionApiService.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairSubscriptionApiService.class).asEagerSingleton();
+ bind(SubscriptionApiService.class).to(DefaultSubscriptionApiService.class).asEagerSingleton();
+
bind(EntitlementService.class).to(Engine.class).asEagerSingleton();
bind(Engine.class).asEagerSingleton();
bind(PlanAligner.class).asEagerSingleton();
bind(AddonUtils.class).asEagerSingleton();
bind(MigrationPlanAligner.class).asEagerSingleton();
+
+ bind(EntitlementRepairApi.class).to(DefaultEntitlementRepairApi.class).asEagerSingleton();
bind(EntitlementUserApi.class).to(DefaultEntitlementUserApi.class).asEagerSingleton();
bind(EntitlementBillingApi.class).to(DefaultEntitlementBillingApi.class).asEagerSingleton();
bind(EntitlementMigrationApi.class).to(DefaultEntitlementMigrationApi.class).asEagerSingleton();
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
index d2bb5b7..2064310 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
@@ -44,7 +44,8 @@ DROP TABLE IF EXISTS bundles;
CREATE TABLE bundles (
id char(36) NOT NULL,
start_dt datetime, /*NOT NULL*/
- name varchar(64) NOT NULL,
+ external_key varchar(64) NOT NULL,
account_id char(36) NOT NULL,
+ last_sys_update_dt datetime,
PRIMARY KEY(id)
) ENGINE=innodb;
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
index 4f0e4db..184604d 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
@@ -4,37 +4,49 @@ insertBundle() ::= <<
insert into bundles (
id
, start_dt
- , name
+ , external_key
, account_id
+ , last_sys_update_dt
) values (
:id
, :start_dt
- , :name
+ , :external_key
, :account_id
+ , :last_sys_update_dt
);
>>
+updateBundleLastSysTime(id, last_sys_update_dt) ::= <<
+ update bundles
+ set
+ last_sys_update_dt = :last_sys_update_dt
+ where id = :id
+ ;
+>>
+
getBundleFromId(id) ::= <<
select
id
, start_dt
- , name
+ , external_key
, account_id
+ , last_sys_update_dt
from bundles
where
id = :id
;
>>
-getBundleFromKey(name) ::= <<
+getBundleFromKey(external_key) ::= <<
select
id
, start_dt
- , name
+ , external_key
, account_id
+ , last_sys_update_dt
from bundles
where
- name = :name
+ external_key = :external_key
;
>>
@@ -43,8 +55,9 @@ getBundleFromAccount(account_id) ::= <<
select
id
, start_dt
- , name
+ , external_key
, account_id
+ , last_sys_update_dt
from bundles
where
account_id = :account_id
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultBillingEvent.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultBillingEvent.java
index 0c16089..df4c822 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultBillingEvent.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultBillingEvent.java
@@ -37,8 +37,8 @@ import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.PhaseType;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
import com.ning.billing.mock.BrainDeadProxyFactory;
import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
index 3650bf9..559b9f9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
@@ -21,6 +21,7 @@ import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import java.util.ArrayList;
+import java.util.LinkedList;
import java.util.List;
import java.util.SortedSet;
import java.util.UUID;
@@ -54,7 +55,7 @@ import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
@@ -72,9 +73,9 @@ public class TestDefaultEntitlementBillingApi {
private static final UUID twoId = new UUID(2L,0L);
private CatalogService catalogService;
- private ArrayList<SubscriptionBundle> bundles;
- private ArrayList<Subscription> subscriptions;
- private ArrayList<SubscriptionEventTransition> transitions;
+ private List<SubscriptionBundle> bundles;
+ private List<Subscription> subscriptions;
+ private List<SubscriptionEventTransition> subscriptionTransitions;
private EntitlementDao dao;
private Clock clock;
@@ -97,12 +98,12 @@ public class TestDefaultEntitlementBillingApi {
@BeforeMethod(alwaysRun=true)
public void setupEveryTime() {
bundles = new ArrayList<SubscriptionBundle>();
- final SubscriptionBundle bundle = new SubscriptionBundleData( zeroId,"TestKey", oneId, clock.getUTCNow().minusDays(4));
+ final SubscriptionBundle bundle = new SubscriptionBundleData( zeroId,"TestKey", oneId, clock.getUTCNow().minusDays(4), null);
bundles.add(bundle);
- transitions = new ArrayList<SubscriptionEventTransition>();
- subscriptions = new ArrayList<Subscription>();
+ subscriptionTransitions = new LinkedList<SubscriptionEventTransition>();
+ subscriptions = new LinkedList<Subscription>();
SubscriptionBuilder builder = new SubscriptionBuilder();
subscriptionStartDate = clock.getUTCNow().minusDays(3);
@@ -110,7 +111,7 @@ public class TestDefaultEntitlementBillingApi {
subscription = new SubscriptionData(builder) {
@Override
public List<SubscriptionEventTransition> getBillingTransitions() {
- return transitions;
+ return subscriptionTransitions;
}
};
@@ -153,7 +154,7 @@ public class TestDefaultEntitlementBillingApi {
String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
SubscriptionEventTransition t = new SubscriptionTransitionData(
zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, null, true);
- transitions.add(t);
+ subscriptionTransitions.add(t);
AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
@@ -175,7 +176,7 @@ public class TestDefaultEntitlementBillingApi {
String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
SubscriptionEventTransition t = new SubscriptionTransitionData(
zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, null, true);
- transitions.add(t);
+ subscriptionTransitions.add(t);
Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
((ZombieControl)account).addResult("getBillCycleDay", 1).addResult("getTimeZone", DateTimeZone.UTC)
@@ -200,7 +201,7 @@ public class TestDefaultEntitlementBillingApi {
String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
SubscriptionEventTransition t = new SubscriptionTransitionData(
zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, null, true);
- transitions.add(t);
+ subscriptionTransitions.add(t);
AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
@@ -222,7 +223,7 @@ public class TestDefaultEntitlementBillingApi {
String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
SubscriptionEventTransition t = new SubscriptionTransitionData(
zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, null, true);
- transitions.add(t);
+ subscriptionTransitions.add(t);
Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
((ZombieControl)account).addResult("getBillCycleDay", 1).addResult("getTimeZone", DateTimeZone.UTC);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepair.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepair.java
new file mode 100644
index 0000000..da619ec
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepair.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.TestApiBase;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.DeletedEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.ExistingEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.NewEvent;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionEvents;
+import com.ning.billing.entitlement.glue.MockEngineModuleSql;
+
+public class TestRepair extends TestApiBase {
+
+ @Override
+ public Injector getInjector() {
+ return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
+ }
+
+ @Test(groups={"slow"})
+ public void testFetchBundleRepair() {
+ try {
+
+ String baseProduct = "Shotgun";
+ BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ Subscription baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+ String aoProduct = "Telescopic-Scope";
+ BillingPeriod aoTerm = BillingPeriod.MONTHLY;
+ String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ List<SubscriptionRepair> subscriptionRepair = bundleRepair.getSubscriptions();
+ assertEquals(subscriptionRepair.size(), 2);
+
+ for (SubscriptionRepair cur : subscriptionRepair) {
+ assertNull(cur.getDeletedEvents());
+ assertNull(cur.getNewEvents());
+
+ List<ExistingEvent> events = cur.getExistingEvents();
+ assertEquals(events.size(), 2);
+ sortExistingEvent(events);
+
+ assertEquals(events.get(0).getSubscriptionTransitionType(), SubscriptionTransitionType.CREATE);
+ assertEquals(events.get(1).getSubscriptionTransitionType(), SubscriptionTransitionType.PHASE);
+ final boolean isBP = cur.getId().equals(baseSubscription.getId());
+ if (isBP) {
+ assertEquals(cur.getId(), baseSubscription.getId());
+
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getProductName(), baseProduct);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getPhaseType(), PhaseType.TRIAL);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getProductCategory(),ProductCategory.BASE);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getPriceListName(), basePriceList);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
+
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getProductName(), baseProduct);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getPhaseType(), PhaseType.EVERGREEN);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getProductCategory(),ProductCategory.BASE);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getPriceListName(), basePriceList);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getBillingPeriod(), baseTerm);
+ } else {
+ assertEquals(cur.getId(), aoSubscription.getId());
+
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getProductName(), aoProduct);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getPhaseType(), PhaseType.DISCOUNT);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getProductCategory(),ProductCategory.ADD_ON);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getPriceListName(), aoPriceList);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getBillingPeriod(), aoTerm);
+
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getProductName(), aoProduct);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getPhaseType(), PhaseType.EVERGREEN);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getProductCategory(),ProductCategory.ADD_ON);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getPriceListName(), aoPriceList);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getBillingPeriod(), aoTerm);
+ }
+ }
+ } catch (Exception e) {
+ Assert.fail(e.getMessage());
+ }
+ }
+
+
+
+ @Test(groups={"slow"})
+ public void testSimpleBPRepairAddChangeBeforePhase() throws Exception {
+
+ String baseProduct = "Shotgun";
+ BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ Subscription baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ // MOVE CLOCK-- STAYS in TRIAL
+ Duration moveTenDays = getDurationDay(10);
+ clock.setDeltaFromReality(moveTenDays, 0);
+
+
+ DateTime changeTime = baseSubscription.getStartDate().plusDays(3);
+ String newBaseProduct = "Assault-Rifle";
+ BillingPeriod newBaseTerm = BillingPeriod.MONTHLY;
+ String newBasePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier(newBaseProduct, ProductCategory.BASE, newBaseTerm, newBasePriceList, PhaseType.TRIAL);
+
+
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, changeTime, spec);
+ DeletedEvent de = createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId());
+ SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), Collections.singletonList(de), Collections.singletonList(ne));
+
+ // FIRST ISSUE DRY RUN
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+
+ boolean dryRun = true;
+ BundleRepair dryRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+ List<SubscriptionRepair> subscriptionRepair = dryRunBundleRepair.getSubscriptions();
+ assertEquals(subscriptionRepair.size(), 1);
+ SubscriptionRepair cur = subscriptionRepair.get(0);
+ assertEquals(cur.getId(), baseSubscription.getId());
+
+ List<ExistingEvent> events = cur.getExistingEvents();
+ assertEquals(events.size(), 3);
+
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getProductName(), baseProduct);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getPhaseType(), PhaseType.TRIAL);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getProductCategory(),ProductCategory.BASE);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getPriceListName(), basePriceList);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
+
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getProductName(), newBaseProduct);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getPhaseType(), PhaseType.TRIAL);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getProductCategory(),ProductCategory.BASE);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getPriceListName(), basePriceList);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
+
+ assertEquals(events.get(2).getPlanPhaseSpecifier().getProductName(), newBaseProduct);
+ assertEquals(events.get(2).getPlanPhaseSpecifier().getPhaseType(), PhaseType.EVERGREEN);
+ assertEquals(events.get(2).getPlanPhaseSpecifier().getProductCategory(),ProductCategory.BASE);
+ assertEquals(events.get(2).getPlanPhaseSpecifier().getPriceListName(), basePriceList);
+ assertEquals(events.get(2).getPlanPhaseSpecifier().getBillingPeriod(), baseTerm);
+
+ SubscriptionData dryRunBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+
+ assertEquals(dryRunBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+ assertEquals(dryRunBaseSubscription.getBundleId(), bundle.getId());
+ assertEquals(dryRunBaseSubscription.getStartDate(), baseSubscription.getStartDate());
+
+ Plan currentPlan = dryRunBaseSubscription.getCurrentPlan();
+ assertNotNull(currentPlan);
+ assertEquals(currentPlan.getProduct().getName(), baseProduct);
+ assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+ assertEquals(currentPlan.getBillingPeriod(), baseTerm);
+
+ PlanPhase currentPhase = dryRunBaseSubscription.getCurrentPhase();
+ assertNotNull(currentPhase);
+ assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
+
+
+ // SECOND RE-ISSUE CALL-- NON DRY RUN
+
+ }
+
+
+ private SubscriptionRepair createSubscriptionReapir(final UUID id, final List<DeletedEvent> deletedEvents, final List<NewEvent> newEvents) {
+ return new SubscriptionRepair() {
+ @Override
+ public UUID getId() {
+ return id;
+ }
+ @Override
+ public List<NewEvent> getNewEvents() {
+ return newEvents;
+ }
+ @Override
+ public List<ExistingEvent> getExistingEvents() {
+ return null;
+ }
+ @Override
+ public List<DeletedEvent> getDeletedEvents() {
+ return deletedEvents;
+ }
+ };
+ }
+
+ private BundleRepair createBundleRepair(final UUID bundleId, final String viewId, final List<SubscriptionRepair> subscriptionRepair) {
+ return new BundleRepair() {
+ @Override
+ public String getViewId() {
+ return viewId;
+ }
+ @Override
+ public List<SubscriptionRepair> getSubscriptions() {
+ return subscriptionRepair;
+ }
+ @Override
+ public UUID getBundleId() {
+ return bundleId;
+ }
+ };
+ }
+
+ private DeletedEvent createDeletedEvent(final UUID eventId) {
+ return new DeletedEvent() {
+ @Override
+ public UUID getEventId() {
+ return eventId;
+ }
+ };
+ }
+
+ private NewEvent createNewEvent(final SubscriptionTransitionType type, final DateTime requestedDate, final PlanPhaseSpecifier spec) {
+
+ return new NewEvent() {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() {
+ return type;
+ }
+ @Override
+ public DateTime getRequestedDate() {
+ return requestedDate;
+ }
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+ return spec;
+ }
+ };
+ }
+
+ private void sortEventsOnBundle(final BundleRepair bundle) {
+ if (bundle.getSubscriptions() == null) {
+ return;
+ }
+ for (SubscriptionRepair cur : bundle.getSubscriptions()) {
+ if (cur.getExistingEvents() != null) {
+ sortExistingEvent(cur.getExistingEvents());
+ }
+ if (cur.getNewEvents() != null) {
+ sortNewEvent(cur.getNewEvents());
+ }
+ }
+ }
+
+ private void sortExistingEvent(final List<ExistingEvent> events) {
+ Collections.sort(events, new Comparator<ExistingEvent>() {
+ @Override
+ public int compare(ExistingEvent arg0, ExistingEvent arg1) {
+ return arg0.getEffectiveDate().compareTo(arg1.getEffectiveDate());
+ }
+ });
+ }
+ private void sortNewEvent(final List<NewEvent> events) {
+ Collections.sort(events, new Comparator<NewEvent>() {
+ @Override
+ public int compare(NewEvent arg0, NewEvent arg1) {
+ return arg0.getRequestedDate().compareTo(arg1.getRequestedDate());
+ }
+ });
+ }
+
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
index 0a65693..eebd3e9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
@@ -59,6 +59,7 @@ import com.ning.billing.dbi.MysqlTestingHelper;
import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
+import com.ning.billing.entitlement.api.repair.EntitlementRepairApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
@@ -90,6 +91,7 @@ public abstract class TestApiBase {
protected EntitlementBillingApi billingApi;
protected EntitlementMigrationApi migrationApi;
+ protected EntitlementRepairApi repairApi;
protected CatalogService catalogService;
protected EntitlementConfig config;
@@ -148,7 +150,7 @@ public abstract class TestApiBase {
((DefaultCatalogService) catalogService).loadCatalog();
((DefaultBusService) busService).startBus();
((Engine) entitlementService).initialize();
- init();
+ init(g);
} catch (Exception e) {
}
}
@@ -167,7 +169,7 @@ public abstract class TestApiBase {
}
}
- private void init() throws Exception {
+ private void init(Injector g) throws Exception {
setupMySQL();
@@ -182,6 +184,8 @@ public abstract class TestApiBase {
entitlementApi = entitlementService.getUserApi();
billingApi = entitlementService.getBillingApi();
migrationApi = entitlementService.getMigrationApi();
+
+ repairApi = g.getInstance(EntitlementRepairApi.class);
}
@BeforeMethod(alwaysRun = true)
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
index 14d6653..258308a 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
@@ -354,6 +354,7 @@ public class TestUserApiAddOn extends TestApiBase {
// MOVE THROUGH TIME TO GO INTO EVERGREEN
someTimeLater = aoCurrentPhase.getDuration();
clock.addDeltaFromReality(someTimeLater);
+ clock.addDeltaFromReality(getDurationDay(1));
assertTrue(testListener.isCompleted(5000));
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
index 1872305..18c3ee3 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
@@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.TreeSet;
import java.util.UUID;
@@ -36,6 +37,7 @@ import com.ning.billing.catalog.api.CatalogService;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.catalog.api.TimeUnit;
import com.ning.billing.config.EntitlementConfig;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.migration.AccountMigrationData;
import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
@@ -43,8 +45,7 @@ import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.engine.core.Engine;
import com.ning.billing.entitlement.events.EntitlementEvent;
import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
@@ -446,4 +447,11 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
public void saveCustomFields(SubscriptionData subscription, CallContext context) {
throw new NotImplementedException();
}
+
+ @Override
+ public Map<UUID, List<EntitlementEvent>> getEventsForBundle(UUID bundleId) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
index 4733f4a..5c5b2b5 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
@@ -26,7 +26,7 @@ import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
import com.google.inject.Inject;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory;
import com.ning.billing.entitlement.engine.addon.AddonUtils;
import com.ning.billing.util.clock.Clock;
import com.ning.billing.util.notificationq.NotificationQueueService;
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
index 8b78c2d..20b91e1 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
@@ -17,8 +17,11 @@
package com.ning.billing.entitlement.glue;
+import com.google.inject.name.Names;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementLifecycleDao;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
import com.ning.billing.entitlement.engine.dao.MockEntitlementDaoMemory;
+import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
import com.ning.billing.util.notificationq.MockNotificationQueueService;
import com.ning.billing.util.notificationq.NotificationQueueService;
@@ -27,6 +30,9 @@ public class MockEngineModuleMemory extends MockEngineModule {
@Override
protected void installEntitlementDao() {
bind(EntitlementDao.class).to(MockEntitlementDaoMemory.class).asEagerSingleton();
+ bind(EntitlementDao.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementLifecycleDao.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementDao.class).asEagerSingleton();
}
private void installNotificationQueue() {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
index f774e58..ebe60c4 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
@@ -16,11 +16,15 @@
package com.ning.billing.entitlement.glue;
+import com.google.inject.name.Names;
import com.ning.billing.dbi.DBIProvider;
import com.ning.billing.dbi.DbiConfig;
import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementLifecycleDao;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.engine.dao.EntitlementSqlDao;
import com.ning.billing.entitlement.engine.dao.MockEntitlementDaoSql;
+import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
import com.ning.billing.util.clock.Clock;
import com.ning.billing.util.clock.ClockMock;
import com.ning.billing.util.glue.FieldStoreModule;
@@ -35,7 +39,11 @@ public class MockEngineModuleSql extends MockEngineModule {
@Override
protected void installEntitlementDao() {
bind(EntitlementDao.class).to(MockEntitlementDaoSql.class).asEagerSingleton();
+ bind(EntitlementDao.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementLifecycleDao.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementDao.class).asEagerSingleton();
}
+
protected void installDBI() {
final MysqlTestingHelper helper = new MysqlTestingHelper();
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
index 512f559..04af58b 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
@@ -48,12 +48,12 @@ import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.billing.BillingModeType;
import com.ning.billing.entitlement.api.billing.DefaultBillingEvent;
import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
import com.ning.billing.invoice.InvoiceDispatcher;
import com.ning.billing.invoice.TestInvoiceDispatcher;
import com.ning.billing.invoice.api.Invoice;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
index 907c8e3..aa8abb5 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
@@ -26,11 +26,11 @@ import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.PhaseType;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.billing.BillingModeType;
import com.ning.billing.entitlement.api.billing.DefaultBillingEvent;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.InvoiceItem;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
index 26df2af..a15b2c1 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
@@ -26,6 +26,8 @@ import java.util.concurrent.Callable;
import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.account.api.MockAccountUserApi;
+import com.ning.billing.entitlement.api.SubscriptionApiService;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.billing.DefaultEntitlementBillingApi;
import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
import com.ning.billing.invoice.InvoiceDispatcher;
@@ -56,16 +58,24 @@ import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Stage;
+import com.google.inject.name.Names;
import com.ning.billing.catalog.DefaultCatalogService;
import com.ning.billing.catalog.api.CatalogService;
import com.ning.billing.config.CatalogConfig;
import com.ning.billing.config.InvoiceConfig;
import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementLifecycleDao;
+import com.ning.billing.entitlement.api.repair.RepairSubscriptionApiService;
+import com.ning.billing.entitlement.api.repair.RepairSubscriptionFactory;
import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionApiService;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
import com.ning.billing.entitlement.engine.dao.EntitlementSqlDao;
+import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
+import com.ning.billing.entitlement.glue.EntitlementModule;
import com.ning.billing.invoice.InvoiceListener;
import com.ning.billing.lifecycle.KillbillService.ServiceException;
import com.ning.billing.mock.BrainDeadProxyFactory;
@@ -88,6 +98,7 @@ public class TestNextBillingDateNotifier {
private InvoiceListenerMock listener;
private NotificationQueueService notificationQueueService;
+
private static final class InvoiceListenerMock extends InvoiceListener {
int eventCount = 0;
UUID latestSubscriptionId = null;
@@ -135,14 +146,21 @@ public class TestNextBillingDateNotifier {
bind(IDBI.class).toInstance(dbi);
bind(TagDao.class).to(AuditedTagDao.class).asEagerSingleton();
bind(EntitlementDao.class).to(EntitlementSqlDao.class).asEagerSingleton();
+ bind(EntitlementDao.class).annotatedWith(Names.named(EntitlementModule.REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementLifecycleDao.class).annotatedWith(Names.named(EntitlementModule.REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementDao.class).asEagerSingleton();
bind(CustomFieldDao.class).to(AuditedCustomFieldDao.class).asEagerSingleton();
bind(GlobalLocker.class).to(MySqlGlobalLocker.class).asEagerSingleton();
bind(InvoiceGenerator.class).to(DefaultInvoiceGenerator.class).asEagerSingleton();
- bind(InvoiceDao.class).to(DefaultInvoiceDao.class);
+ bind(InvoiceDao.class).to(DefaultInvoiceDao.class).asEagerSingleton();
bind(NextBillingDatePoster.class).to(DefaultNextBillingDatePoster.class).asEagerSingleton();
bind(AccountUserApi.class).to(MockAccountUserApi.class).asEagerSingleton();
bind(EntitlementBillingApi.class).to(DefaultEntitlementBillingApi.class).asEagerSingleton();
- bind(EntitlementUserApi.class).to(DefaultEntitlementUserApi.class).asEagerSingleton();
+ bind(EntitlementUserApi.class).to(DefaultEntitlementUserApi.class).asEagerSingleton();
+ bind(SubscriptionApiService.class).annotatedWith(Names.named(EntitlementModule.REPAIR_NAMED)).to(RepairSubscriptionApiService.class).asEagerSingleton();
+ bind(SubscriptionApiService.class).to(DefaultSubscriptionApiService.class).asEagerSingleton();
+ bind(SubscriptionFactory.class).annotatedWith(Names.named(EntitlementModule.REPAIR_NAMED)).to(RepairSubscriptionFactory.class).asEagerSingleton();
+ bind(SubscriptionFactory.class).to(DefaultSubscriptionFactory.class).asEagerSingleton();
}
});
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
index b77f758..c13e386 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -48,12 +48,12 @@ import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.billing.BillingModeType;
import com.ning.billing.entitlement.api.billing.DefaultBillingEvent;
import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.InvoiceUserApi;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
index da29ce7..5792abd 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
@@ -27,13 +27,13 @@ import com.ning.billing.catalog.api.PhaseType;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.config.InvoiceConfig;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.billing.BillingModeType;
import com.ning.billing.entitlement.api.billing.DefaultBillingEvent;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.model.BillingEventSet;