killbill-uncached
Changes
api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApiException.java 38(+38 -0)
entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java 88(+88 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java 80(+80 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java 253(+252 -1)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java 13(+10 -3)
entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg 6(+6 -0)
Details
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApi.java b/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApi.java
index 7d41f08..b4f13a8 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApi.java
@@ -60,13 +60,15 @@ public interface EntitlementMigrationApi {
*
*/
- public void migrate(EntitlementAccountMigration toBeMigrated);
+ public void migrate(EntitlementAccountMigration toBeMigrated)
+ throws EntitlementMigrationApiException;
/**
* Remove all the data pertaining to that acount
*
* @param accountKey
*/
- public void undoMigration(UUID accountKey);
+ public void undoMigration(UUID accountKey)
+ throws EntitlementMigrationApiException;
}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApiException.java b/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApiException.java
new file mode 100644
index 0000000..55835fd
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/migration/EntitlementMigrationApiException.java
@@ -0,0 +1,38 @@
+/*
+ * 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.migration;
+
+public class EntitlementMigrationApiException extends Exception {
+
+ private static final long serialVersionUID = 7623133L;
+
+ public EntitlementMigrationApiException() {
+ super();
+ }
+
+ public EntitlementMigrationApiException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public EntitlementMigrationApiException(String message) {
+ super(message);
+ }
+
+ public EntitlementMigrationApiException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
index 00e478f..770bdb8 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
@@ -28,6 +28,7 @@ import com.ning.billing.util.eventbus.EventBusNotification;
public interface SubscriptionTransition extends EventBusNotification {
public enum SubscriptionTransitionType {
+ MIGRATE_ENTITLEMENT,
CREATE,
CHANGE,
PAUSE,
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java
new file mode 100644
index 0000000..e50988b
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java
@@ -0,0 +1,88 @@
+/*
+ * 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.alignment;
+
+import org.joda.time.DateTime;
+
+import com.google.inject.Inject;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApiException;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
+import com.ning.billing.entitlement.events.user.ApiEventType;
+
+public class MigrationPlanAligner {
+
+ private final CatalogService catalogService;
+
+ @Inject
+ public MigrationPlanAligner(CatalogService catalogService) {
+ this.catalogService = catalogService;
+ }
+
+
+ public TimedMigration [] getEventsOnRegularMigration(SubscriptionData subscription,
+ Plan plan, PlanPhase initialPhase, String priceList, DateTime effectiveDate) {
+ TimedMigration [] result = new TimedMigration[1];
+ result[0] = new TimedMigration(effectiveDate, EventType.API_USER, ApiEventType.MIGRATE_ENTITLEMENT, plan, initialPhase, priceList);
+ return result;
+ }
+
+ public TimedMigration [] getEventsOnFuturePhaseChangeMigration(SubscriptionData subscription,
+ Plan plan, PlanPhase initialPhase, String priceList, DateTime effectiveDate, DateTime effectiveDateForNextPhase)
+ throws EntitlementMigrationApiException {
+
+ TimedMigration [] result = new TimedMigration[2];
+ result[0] = new TimedMigration(effectiveDate, EventType.API_USER, ApiEventType.MIGRATE_ENTITLEMENT, plan, initialPhase, priceList);
+ boolean foundCurrent = false;
+ PlanPhase nextPhase = null;
+ for (PlanPhase cur : plan.getAllPhases()) {
+ if (cur == initialPhase) {
+ foundCurrent = true;
+ continue;
+ }
+ if (foundCurrent) {
+ nextPhase = cur;
+ }
+ }
+ if (nextPhase == null) {
+ throw new EntitlementMigrationApiException(String.format("Cannot find next phase for Plan %s and current Phase %s",
+ plan.getName(), initialPhase.getName()));
+ }
+ result[1] = new TimedMigration(effectiveDateForNextPhase, EventType.PHASE, null, plan, nextPhase, priceList);
+ return result;
+ }
+
+ public TimedMigration [] getEventsOnFuturePlanChangeMigration(SubscriptionData subscription,
+ Plan currentPlan, PlanPhase currentPhase, Plan newPlan, String priceList, DateTime effectiveDate, DateTime effectiveDateForChangePlan) {
+ TimedMigration [] result = new TimedMigration[2];
+ result[0] = new TimedMigration(effectiveDate, EventType.API_USER, ApiEventType.MIGRATE_ENTITLEMENT, currentPlan, currentPhase, priceList);
+ PlanPhase newPlanPhase = newPlan.getAllPhases()[0];
+ result[1] = new TimedMigration(effectiveDate, EventType.API_USER, ApiEventType.CHANGE, newPlan, newPlanPhase, priceList);
+ return result;
+ }
+
+ public TimedMigration [] getEventsOnFuturePlanCancelMigration(SubscriptionData subscription,
+ Plan plan, PlanPhase initialPhase, String priceList, DateTime effectiveDate, DateTime effectiveDateForCancellation) {
+ TimedMigration [] result = new TimedMigration[2];
+ result[0] = new TimedMigration(effectiveDate, EventType.API_USER, ApiEventType.MIGRATE_ENTITLEMENT, plan, initialPhase, priceList);
+ result[1] = new TimedMigration(effectiveDate, EventType.API_USER, ApiEventType.CANCEL, null, null, null);
+ return result;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/TimedMigration.java b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/TimedMigration.java
new file mode 100644
index 0000000..e1ad564
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/TimedMigration.java
@@ -0,0 +1,72 @@
+/*
+ * 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.alignment;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
+import com.ning.billing.entitlement.events.user.ApiEventType;
+
+public class TimedMigration {
+
+ private final DateTime eventTime;
+ private final EventType eventType;
+ private final ApiEventType apiEventType;
+
+ private final Plan plan;
+ private final PlanPhase phase;
+ private final String priceList;
+
+
+ public TimedMigration(DateTime eventTime, EventType eventType,
+ ApiEventType apiEventType, Plan plan, PlanPhase phase, String priceList) {
+ super();
+ this.eventTime = eventTime;
+ this.eventType = eventType;
+ this.apiEventType = apiEventType;
+ this.plan = plan;
+ this.phase = phase;
+ this.priceList = priceList;
+ }
+
+ public DateTime getEventTime() {
+ return eventTime;
+ }
+
+ public EventType getEventType() {
+ return eventType;
+ }
+
+
+ public ApiEventType getApiEventType() {
+ return apiEventType;
+ }
+
+ public Plan getPlan() {
+ return plan;
+ }
+
+ public PlanPhase getPhase() {
+ return phase;
+ }
+
+ public String getPriceList() {
+ return priceList;
+ }
+}
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
new file mode 100644
index 0000000..7396136
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java
@@ -0,0 +1,80 @@
+/*
+ * 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.migration;
+
+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.events.EntitlementEvent;
+
+public class AccountMigrationData {
+
+ private final List<BundleMigrationData> data;
+
+ public AccountMigrationData(List<BundleMigrationData> data) {
+ super();
+ this.data = data;
+ }
+
+ public List<BundleMigrationData> getData() {
+ return data;
+ }
+
+ public static class BundleMigrationData {
+
+ private final SubscriptionBundleData data;
+ private final List<SubscriptionMigrationData> subscriptions;
+
+ public BundleMigrationData(SubscriptionBundleData data,
+ List<SubscriptionMigrationData> subscriptions) {
+ super();
+ this.data = data;
+ this.subscriptions = subscriptions;
+ }
+
+ public SubscriptionBundleData getData() {
+ return data;
+ }
+
+ public List<SubscriptionMigrationData> getSubscriptions() {
+ return subscriptions;
+ }
+ }
+
+ public static class SubscriptionMigrationData {
+
+ private final SubscriptionData data;
+ private final List<EntitlementEvent> initialEvents;
+
+ public SubscriptionMigrationData(SubscriptionData data,
+
+ List<EntitlementEvent> initialEvents) {
+ super();
+ this.data = data;
+ this.initialEvents = initialEvents;
+ }
+
+ public SubscriptionData getData() {
+ return data;
+ }
+
+ public List<EntitlementEvent> getInitialEvents() {
+ return initialEvents;
+ }
+ }
+}
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 a5595e6..211ba43 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
@@ -16,15 +16,266 @@
package com.ning.billing.entitlement.api.migration;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
import java.util.UUID;
+import org.joda.time.DateTime;
+
+import com.google.inject.Inject;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.CatalogService;
+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.ProductCategory;
+import com.ning.billing.entitlement.alignment.MigrationPlanAligner;
+import com.ning.billing.entitlement.alignment.TimedMigration;
+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.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
+import com.ning.billing.entitlement.events.phase.PhaseEvent;
+import com.ning.billing.entitlement.events.phase.PhaseEventData;
+import com.ning.billing.entitlement.events.user.ApiEventBuilder;
+import com.ning.billing.entitlement.events.user.ApiEventCancel;
+import com.ning.billing.entitlement.events.user.ApiEventChange;
+import com.ning.billing.entitlement.events.user.ApiEventMigrate;
+import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.util.clock.Clock;
+
public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
+
+ private final EntitlementDao dao;
+ private final MigrationPlanAligner migrationAligner;
+ private final SubscriptionFactory factory;
+ private final CatalogService catalogService;
+ private final Clock clock;
+
+ @Inject
+ public DefaultEntitlementMigrationApi(MigrationPlanAligner migrationAligner,
+ SubscriptionFactory factory,
+ CatalogService catalogService,
+ EntitlementDao dao,
+ Clock clock) {
+ this.dao = dao;
+ this.migrationAligner = migrationAligner;
+ this.factory = factory;
+ this.catalogService = catalogService;
+ this.clock = clock;
+ }
+
@Override
- public void migrate(EntitlementAccountMigration toBeMigrated) {
+ public void migrate(EntitlementAccountMigration toBeMigrated)
+ throws EntitlementMigrationApiException {
+ AccountMigrationData accountMigrationData = createAccountMigrationData(toBeMigrated);
+ dao.migrate(accountMigrationData);
}
@Override
public void undoMigration(UUID accountKey) {
}
+
+ private AccountMigrationData createAccountMigrationData(EntitlementAccountMigration toBeMigrated)
+ throws EntitlementMigrationApiException {
+
+ final UUID accountId = toBeMigrated.getAccountKey();
+ final DateTime now = clock.getUTCNow();
+
+ List<BundleMigrationData> accountBundleData = new LinkedList<BundleMigrationData>();
+
+ for (final EntitlementBundleMigration curBundle : toBeMigrated.getBundles()) {
+
+ SubscriptionBundleData bundleData = new SubscriptionBundleData(curBundle.getBundleKey(), accountId);
+ List<SubscriptionMigrationData> bundleSubscriptionData = new LinkedList<AccountMigrationData.SubscriptionMigrationData>();
+
+ for (EntitlementSubscriptionMigration curSub : curBundle.getSubscriptions()) {
+ SubscriptionMigrationData data = null;
+ switch (curSub.getCategory()) {
+ case BASE:
+ data = createBaseSubscriptionMigrationData(bundleData.getId(), curSub.getCategory(), curSub.getSubscriptionCases(), now);
+ break;
+ case ADD_ON:
+ // Not implemented yet
+ break;
+ case STANDALONE:
+ // Not implemented yet
+ break;
+ default:
+ throw new EntitlementMigrationApiException(String.format("Unkown product type ", curSub.getCategory()));
+ }
+ if (data != null) {
+ bundleSubscriptionData.add(data);
+ }
+ }
+ BundleMigrationData bundleMigrationData = new BundleMigrationData(bundleData, bundleSubscriptionData);
+ accountBundleData.add(bundleMigrationData);
+ }
+ AccountMigrationData accountMigrationData = new AccountMigrationData(accountBundleData);
+ return accountMigrationData;
+ }
+
+ private SubscriptionMigrationData createBaseSubscriptionMigrationData(UUID bundleId, ProductCategory productCategory,
+ EntitlementSubscriptionMigrationCase [] input, DateTime now)
+ throws EntitlementMigrationApiException {
+
+ try {
+ // STEPH ah... what is that exactly?
+ final DateTime bundleStartDate = now;
+
+ List<EntitlementEvent> emptyEvents = Collections.emptyList();
+
+ SubscriptionData subscriptionData = factory.createSubscription(new SubscriptionBuilder()
+ .setId(UUID.randomUUID())
+ .setBundleId(bundleId)
+ .setCategory(productCategory)
+ .setBundleStartDate(bundleStartDate)
+ // STEPH
+ /* .setStartDate(effectiveDate) */,
+ emptyEvents);
+
+ TimedMigration [] events = null;
+ Plan plan0 = catalogService.getCatalog().findPlan(input[0].getPlanPhaseSpecifer().getProductName(),
+ input[0].getPlanPhaseSpecifer().getBillingPeriod(), input[0].getPlanPhaseSpecifer().getPriceListName());
+
+ Plan plan1 = (input.length > 1) ? catalogService.getCatalog().findPlan(input[1].getPlanPhaseSpecifer().getProductName(),
+ input[1].getPlanPhaseSpecifer().getBillingPeriod(), input[1].getPlanPhaseSpecifer().getPriceListName()) :
+ null;
+
+ if (isRegularMigratedSubscription(input)) {
+
+ events = migrationAligner.getEventsOnRegularMigration(subscriptionData,
+ plan0,
+ getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
+ input[0].getPlanPhaseSpecifer().getPriceListName(),
+ now);
+
+ } else if (isRegularFutureCancelledMigratedSubscription(input)) {
+
+ events = migrationAligner.getEventsOnFuturePlanCancelMigration(subscriptionData,
+ plan0,
+ getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
+ input[0].getPlanPhaseSpecifer().getPriceListName(),
+ now,
+ input[0].getCancelledDate());
+
+ } else if (isPhaseChangeMigratedSubscription(input)) {
+
+ events = migrationAligner.getEventsOnFuturePhaseChangeMigration(subscriptionData,
+ plan0,
+ getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
+ input[0].getPlanPhaseSpecifer().getPriceListName(),
+ now,
+ input[1].getEffectiveDate());
+
+ } else if (isPlanChangeMigratedSubscription(input)) {
+
+ events = migrationAligner.getEventsOnFuturePlanChangeMigration(subscriptionData,
+ plan0,
+ getPlanPhase(plan0, input[0].getPlanPhaseSpecifer().getPhaseType()),
+ plan1,
+ input[0].getPlanPhaseSpecifer().getPriceListName(),
+ now,
+ input[1].getEffectiveDate());
+
+ } else {
+ throw new EntitlementMigrationApiException("Unknown migration type");
+ }
+ return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, events));
+ } catch (CatalogApiException e) {
+ throw new EntitlementMigrationApiException(e);
+ }
+ }
+
+ // STEPH should be in catalog
+ private PlanPhase getPlanPhase(Plan plan, PhaseType phaseType) throws EntitlementMigrationApiException {
+ for (PlanPhase cur: plan.getAllPhases()) {
+ if (cur.getPhaseType() == phaseType) {
+ return cur;
+ }
+ }
+ throw new EntitlementMigrationApiException(String.format("Cannot find PlanPhase from Plan %s and type %s", plan.getName(), phaseType));
+ }
+
+ private List<EntitlementEvent> toEvents(SubscriptionData subscriptionData, DateTime now, TimedMigration [] migrationEvents) {
+
+
+ List<EntitlementEvent> events = new ArrayList<EntitlementEvent>(migrationEvents.length);
+ for (TimedMigration cur : migrationEvents) {
+
+ if (cur.getEventType() == EventType.PHASE) {
+ PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(cur.getPhase().getName(), subscriptionData, now, cur.getEventTime());
+ events.add(nextPhaseEvent);
+
+ } else if (cur.getEventType() == EventType.API_USER) {
+
+ ApiEventBuilder builder = new ApiEventBuilder()
+ .setSubscriptionId(subscriptionData.getId())
+ .setEventPlan(cur.getPlan().getName())
+ .setEventPlanPhase(cur.getPhase().getName())
+ .setEventPriceList(cur.getPriceList())
+ .setActiveVersion(subscriptionData.getActiveVersion())
+ .setEffectiveDate(cur.getEventTime())
+ .setProcessedDate(now)
+ .setRequestedDate(now);
+
+ switch(cur.getApiEventType()) {
+ case MIGRATE_ENTITLEMENT:
+ events.add(new ApiEventMigrate(builder));
+ break;
+
+ case CHANGE:
+ events.add(new ApiEventChange(builder));
+ break;
+ case CANCEL:
+ events.add(new ApiEventCancel(builder));
+ break;
+ default:
+ throw new EntitlementError(String.format("Unexpected type of api migration event %s", cur.getApiEventType()));
+ }
+ } else {
+ throw new EntitlementError(String.format("Unexpected type of migration event %s", cur.getEventType()));
+ }
+ }
+ return events;
+ }
+
+ private boolean isRegularMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
+ return (input.length == 1 && input[0].getCancelledDate() == null);
+ }
+
+ private boolean isRegularFutureCancelledMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
+ return (input.length == 1 && input[0].getCancelledDate() != null);
+ }
+
+ private boolean isPhaseChangeMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
+ if (input.length != 2) {
+ return false;
+ }
+ return isSamePlan(input[0].getPlanPhaseSpecifer(), input[1].getPlanPhaseSpecifer());
+ }
+
+ private boolean isPlanChangeMigratedSubscription(EntitlementSubscriptionMigrationCase [] input) {
+ if (input.length != 2) {
+ return false;
+ }
+ return ! isSamePlan(input[0].getPlanPhaseSpecifer(), input[1].getPlanPhaseSpecifer());
+ }
+
+ private boolean isSamePlan(PlanPhaseSpecifier plan0, PlanPhaseSpecifier plan1) {
+ if (plan0.getPriceListName().equals(plan1.getPriceListName()) &&
+ plan0.getProductName().equals(plan1.getProductName())) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
index 1561aec..cc390d9 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
@@ -88,7 +88,10 @@ public class SubscriptionApiService {
.setEffectiveDate(effectiveDate)
.setRequestedDate(requestedDate));
- PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(curAndNextPhases[1], subscription, processedDate);
+ TimedPhase nextTimedPhase = curAndNextPhases[1];
+ PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
+ PhaseEventData.getNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, processedDate, nextTimedPhase.getStartPhase()) :
+ null;
List<EntitlementEvent> events = new ArrayList<EntitlementEvent>();
events.add(creationEvent);
if (nextPhaseEvent != null) {
@@ -163,7 +166,9 @@ public class SubscriptionApiService {
DateTime planStartDate = subscription.getCurrentPlanStart();
TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription.getCurrentPlan(), subscription.getInitialPhaseOnCurrentPlan().getPhaseType(), now, planStartDate);
- PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(nextTimedPhase, subscription, now);
+ PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
+ PhaseEventData.getNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
+ null;
if (nextPhaseEvent != null) {
uncancelEvents.add(nextPhaseEvent);
}
@@ -227,7 +232,9 @@ public class SubscriptionApiService {
.setRequestedDate(now));
TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), effectiveDate);
- PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(nextTimedPhase, subscription, now);
+ PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
+ PhaseEventData.getNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
+ null;
List<EntitlementEvent> changeEvents = new ArrayList<EntitlementEvent>();
// Only add the PHASE if it does not coincide with the CHANGE, if not this is 'just' a CHANGE.
if (nextPhaseEvent != null && ! nextPhaseEvent.getEffectiveDate().equals(changeEvent.getEffectiveDate())) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionFactory.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionFactory.java
index 48008bf..0f5987d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionFactory.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionFactory.java
@@ -44,12 +44,13 @@ public class SubscriptionFactory {
public SubscriptionData createSubscription(SubscriptionBuilder builder, List<EntitlementEvent> events) {
SubscriptionData subscription = new SubscriptionData(builder, apiService, clock);
- subscription.rebuildTransitions(events, catalogService.getCatalog());
+ if (events.size() > 0) {
+ subscription.rebuildTransitions(events, catalogService.getCatalog());
+ }
return subscription;
}
-
public static class SubscriptionBuilder {
private UUID id;
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 89b4f2b..9cda86d 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
@@ -180,7 +180,9 @@ public class Engine implements EventListener, EntitlementService {
try {
DateTime now = clock.getUTCNow();
TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription.getCurrentPlan(), subscription.getInitialPhaseOnCurrentPlan().getPhaseType(), now, subscription.getCurrentPlanStart());
- PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(nextTimedPhase, subscription, now);
+ PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
+ PhaseEventData.getNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
+ null;
if (nextPhaseEvent != null) {
dao.createNextPhaseEvent(subscription.getId(), nextPhaseEvent);
}
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 10e0348..0bafa09 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
@@ -47,6 +47,9 @@ public interface BundleSqlDao extends Transactional<EventSqlDao>, CloseMe, Trans
@SqlUpdate
public void insertBundle(@Bind(binder = SubscriptionBundleBinder.class) SubscriptionBundleData bundle);
+ @SqlUpdate
+ public void removeBundle(@Bind("id") String id);
+
@SqlQuery
@Mapper(ISubscriptionBundleSqlMapper.class)
public SubscriptionBundle getBundleFromId(@Bind("id") String id);
@@ -59,7 +62,6 @@ public interface BundleSqlDao extends Transactional<EventSqlDao>, CloseMe, Trans
@Mapper(ISubscriptionBundleSqlMapper.class)
public List<SubscriptionBundle> getBundleFromAccount(@Bind("account_id") String accountId);
-
public static class SubscriptionBundleBinder implements Binder<Bind, SubscriptionBundleData> {
private Date getDate(DateTime dateTime) {
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 835da51..915138c 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
@@ -20,6 +20,7 @@ import java.util.Collection;
import java.util.List;
import java.util.UUID;
+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.SubscriptionData;
@@ -68,4 +69,8 @@ public interface EntitlementDao {
public void uncancelSubscription(UUID subscriptionId, List<EntitlementEvent> uncancelEvents);
public void changePlan(UUID subscriptionId, List<EntitlementEvent> changeEvents);
+
+ public void migrate(AccountMigrationData data);
+
+ public void undoMigration(List<UUID> bundleIds, List<UUID> subscriptionIds);
}
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 15f1e8c..791ab23 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
@@ -32,6 +32,9 @@ import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.config.EntitlementConfig;
+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;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
@@ -339,4 +342,56 @@ public class EntitlementSqlDao implements EntitlementDao {
}
return result;
}
+
+ @Override
+ public void migrate(final AccountMigrationData accountData) {
+
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+
+ @Override
+ public Void inTransaction(EventSqlDao transEventDao,
+ TransactionStatus status) throws Exception {
+
+ SubscriptionSqlDao transSubDao = transEventDao.become(SubscriptionSqlDao.class);
+ BundleSqlDao transBundleDao = transEventDao.become(BundleSqlDao.class);
+
+ for (BundleMigrationData curBundle : accountData.getData()) {
+ SubscriptionBundleData bundleData = curBundle.getData();
+ for (SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
+ SubscriptionData subData = curSubscription.getData();
+ for (EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
+ transEventDao.insertEvent(curEvent);
+ }
+ transSubDao.insertSubscription(subData);
+ }
+ transBundleDao.insertBundle(bundleData);
+ }
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void undoMigration(final List<UUID> bundleIds, final List<UUID> subscriptionIds) {
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+
+ @Override
+ public Void inTransaction(EventSqlDao transEventDao,
+ TransactionStatus status) throws Exception {
+
+ SubscriptionSqlDao transSubDao = transEventDao.become(SubscriptionSqlDao.class);
+ BundleSqlDao transBundleDao = transEventDao.become(BundleSqlDao.class);
+
+ for (UUID curBundle : bundleIds) {
+ transBundleDao.removeBundle(curBundle.toString());
+ }
+
+ for (UUID curSubscription : subscriptionIds) {
+ transSubDao.removeSubscription(curSubscription.toString());
+ eventsDao.removeEvents(curSubscription.toString());
+ }
+ return null;
+ }
+ });
+ }
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
index a1e2670..300b841 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
@@ -70,6 +70,9 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
public int claimEvent(@Bind("owner") String owner, @Bind("next_available") Date nextAvailable, @Bind("event_id") String eventId, @Bind("now") Date now);
@SqlUpdate
+ public void removeEvents(@Bind("subscription_id") String subscriptionId);
+
+ @SqlUpdate
public void clearEvent(@Bind("event_id") String eventId, @Bind("owner") String owner);
@SqlUpdate
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 4a5312c..2741a10 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
@@ -50,6 +50,9 @@ public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, C
@SqlUpdate
public void insertSubscription(@Bind(binder = ISubscriptionDaoBinder.class) SubscriptionData sub);
+ @SqlUpdate
+ public void removeSubscription(@Bind("id") String id);
+
@SqlQuery
@Mapper(ISubscriptionDaoSqlMapper.class)
public Subscription getSubscriptionFromId(@Bind("id") String id);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventData.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventData.java
index 873fb6d..074dc75 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventData.java
@@ -57,15 +57,15 @@ public class PhaseEventData extends EventBase implements PhaseEvent {
+ ", isActive()=" + isActive() + "]\n";
}
- public static final PhaseEvent getNextPhaseEvent(TimedPhase nextTimedPhase, SubscriptionData subscription, DateTime now) {
- return (nextTimedPhase == null) ?
+ public static final PhaseEvent getNextPhaseEvent(String phaseName, SubscriptionData subscription, DateTime now, DateTime effectiveDate) {
+ return (phaseName == null) ?
null :
new PhaseEventData(new PhaseEventBuilder()
.setSubscriptionId(subscription.getId())
.setRequestedDate(now)
- .setEffectiveDate(nextTimedPhase.getStartPhase())
+ .setEffectiveDate(effectiveDate)
.setProcessedDate(now)
.setActiveVersion(subscription.getActiveVersion())
- .setPhaseName(nextTimedPhase.getPhase().getName()));
+ .setPhaseName(phaseName));
}
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventMigrate.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventMigrate.java
new file mode 100644
index 0000000..636a940
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventMigrate.java
@@ -0,0 +1,24 @@
+/*
+ * 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.events.user;
+
+public class ApiEventMigrate extends ApiEventBase {
+
+ public ApiEventMigrate(ApiEventBuilder builder) {
+ super(builder.setEventType(ApiEventType.MIGRATE_ENTITLEMENT));
+ }
+}
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 c28941e..b994899 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
@@ -20,6 +20,10 @@ import com.ning.billing.entitlement.api.user.SubscriptionTransition.Subscription
public enum ApiEventType {
+ MIGRATE_ENTITLEMENT {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.MIGRATE_ENTITLEMENT; }
+ },
CREATE {
@Override
public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.CREATE; }
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 fd2290f..dc3c102 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
@@ -20,6 +20,7 @@ import org.skife.config.ConfigurationObjectFactory;
import com.google.inject.AbstractModule;
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.billing.DefaultEntitlementBillingApi;
@@ -66,6 +67,7 @@ public class EntitlementModule extends AbstractModule {
bind(EntitlementService.class).to(Engine.class).asEagerSingleton();
bind(Engine.class).asEagerSingleton();
bind(PlanAligner.class).asEagerSingleton();
+ bind(MigrationPlanAligner.class).asEagerSingleton();
bind(EntitlementTestApi.class).to(DefaultEntitlementTestApi.class).asEagerSingleton();
bind(EntitlementUserApi.class).to(DefaultEntitlementUserApi.class).asEagerSingleton();
bind(EntitlementBillingApi.class).to(DefaultEntitlementBillingApi.class).asEagerSingleton();
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 cb15e9b..d9f26c2 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
@@ -14,6 +14,12 @@ insertBundle() ::= <<
);
>>
+removeBundle(id) ::= <<
+ delete from bundles
+ where
+ id = :id
+ ;
+>>
getBundleFromId(id) ::= <<
select
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
index a3b677a..31e9eaf 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
@@ -96,6 +96,13 @@ insertEvent() ::= <<
);
>>
+removeEvents(subscription_id) ::= <<
+ delete from events
+ where
+ subscription_id = :subscription_id
+ ;
+>>
+
insertClaimedHistory(sequence_id, owner_id, hostname, claimed_dt, event_id) ::= <<
insert into claimed_events (
sequence_id
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
index 094bdf1..aee490c 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
@@ -22,6 +22,13 @@ insertSubscription() ::= <<
);
>>
+removeSubscription(id) ::= <<
+ delete from subscriptions
+ where
+ where id = :id
+ ;
+>>
+
getSubscriptionFromId(id) ::= <<
select
id
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 f3f42f6..d36b443 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
@@ -35,6 +35,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.migration.AccountMigrationData;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionApiService;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
@@ -349,4 +350,12 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
}
}
+ @Override
+ public void migrate(AccountMigrationData data) {
+ }
+
+ @Override
+ public void undoMigration(List<UUID> bundleIds, List<UUID> subscriptionIds) {
+ }
+
}