killbill-uncached
Changes
entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java 8(+2 -6)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java 38(+6 -32)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java 259(+259 -0)
entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg 2(+1 -1)
entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg 2(+1 -1)
Details
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 595a2ff..15442ed 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
@@ -28,17 +28,14 @@ import com.ning.billing.entitlement.api.user.SubscriptionBuilder;
import com.ning.billing.entitlement.api.user.SubscriptionData;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
-import com.ning.billing.util.clock.Clock;
public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
- private final Clock clock;
private final EntitlementDao dao;
@Inject
- public DefaultEntitlementBillingApi(Clock clock, EntitlementDao dao) {
+ public DefaultEntitlementBillingApi(EntitlementDao dao) {
super();
- this.clock = clock;
this.dao = dao;
}
@@ -63,7 +60,6 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
SubscriptionBuilder builder = new SubscriptionBuilder(subscription)
.setChargedThroughDate(ctd)
.setPaidThroughDate(subscription.getPaidThroughDate());
- dao.updateSubscription(new SubscriptionData(builder, false));
+ dao.updateSubscription(new SubscriptionData(builder));
}
-
}
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 4fd2cd0..16e3de5 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
@@ -16,7 +16,6 @@
package com.ning.billing.entitlement.api.user;
-import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -31,18 +30,11 @@ import com.ning.billing.catalog.api.ICatalogService;
import com.ning.billing.catalog.api.IPlan;
import com.ning.billing.catalog.api.IPlanPhase;
import com.ning.billing.catalog.api.IPriceListSet;
-import com.ning.billing.catalog.api.PlanAlignmentChange;
import com.ning.billing.entitlement.alignment.PlanAligner;
-import com.ning.billing.entitlement.alignment.TimedPhase;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
-import com.ning.billing.entitlement.events.EntitlementEvent;
-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.ApiEventCreate;
import com.ning.billing.entitlement.exceptions.EntitlementError;
import com.ning.billing.util.clock.DefaultClock;
import com.ning.billing.util.clock.Clock;
@@ -53,12 +45,14 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
private final EntitlementDao dao;
private final PlanAligner planAligner;
private final ICatalogService catalogService;
+ private final SubscriptionApiService apiService;
@Inject
public DefaultEntitlementUserApi(Clock clock, PlanAligner planAligner,
- EntitlementDao dao, ICatalogService catalogService) {
+ EntitlementDao dao, ICatalogService catalogService, SubscriptionApiService apiService) {
super();
this.clock = clock;
+ this.apiService = apiService;
this.dao = dao;
this.planAligner = planAligner;
this.catalogService = catalogService;
@@ -157,34 +151,14 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
}
DateTime effectiveDate = requestedDate;
- SubscriptionData subscription = new SubscriptionData(new SubscriptionBuilder()
+ SubscriptionData subscription = apiService.create(new SubscriptionBuilder()
.setId(UUID.randomUUID())
.setBundleId(bundleId)
.setCategory(plan.getProduct().getCategory())
.setBundleStartDate(bundleStartDate)
.setStartDate(effectiveDate),
- false);
-
- TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnCreate(subscription, plan, realPriceList, effectiveDate);
- ApiEventCreate creationEvent = new ApiEventCreate(new ApiEventBuilder()
- .setSubscriptionId(subscription.getId())
- .setEventPlan(plan.getName())
- .setEventPlanPhase(currentTimedPhase.getPhase().getName())
- .setEventPriceList(realPriceList)
- .setActiveVersion(subscription.getActiveVersion())
- .setProcessedDate(now)
- .setEffectiveDate(effectiveDate)
- .setRequestedDate(requestedDate));
-
- TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnCreate(subscription, plan, realPriceList, effectiveDate);
- PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(nextTimedPhase, subscription, now);
- List<EntitlementEvent> events = new ArrayList<EntitlementEvent>();
- events.add(creationEvent);
- if (nextPhaseEvent != null) {
- events.add(nextPhaseEvent);
- }
+ plan, realPriceList, requestedDate, effectiveDate, now);
- // STEPH Also update startDate for bundle ?
- return dao.createSubscription(subscription, events);
+ return subscription;
}
}
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
new file mode 100644
index 0000000..334d066
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
@@ -0,0 +1,259 @@
+/*
+ * 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.user;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.joda.time.DateTime;
+
+import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.ActionPolicy;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.ICatalogService;
+import com.ning.billing.catalog.api.IPlan;
+import com.ning.billing.catalog.api.IPriceList;
+import com.ning.billing.catalog.api.IProduct;
+import com.ning.billing.catalog.api.PlanChangeResult;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PlanSpecifier;
+import com.ning.billing.entitlement.alignment.PlanAligner;
+import com.ning.billing.entitlement.alignment.TimedPhase;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+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.ApiEventCreate;
+import com.ning.billing.entitlement.events.user.ApiEventUncancel;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.DefaultClock;
+
+public class SubscriptionApiService {
+
+ private final Clock clock;
+ private final EntitlementDao dao;
+ private final ICatalogService catalogService;
+ private final PlanAligner planAligner;
+
+ @Inject
+ public SubscriptionApiService(Clock clock, EntitlementDao dao, ICatalogService catalogService, PlanAligner planAligner) {
+ this.clock = clock;
+ this.catalogService = catalogService;
+ this.planAligner = planAligner;
+ this.dao = dao;
+ }
+
+
+
+ public SubscriptionData createFromExisting(SubscriptionBuilder builder, List<EntitlementEvent> events) {
+ SubscriptionData subscription = new SubscriptionData(builder, this, clock);
+ subscription.rebuildTransitions(events, catalogService.getCatalog());
+ return subscription;
+ }
+
+ public SubscriptionData create(SubscriptionBuilder builder, IPlan plan, String realPriceList,
+ DateTime requestedDate, DateTime effectiveDate, DateTime processedDate) {
+ SubscriptionData subscription = new SubscriptionData(builder, this, clock);
+
+
+ TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnCreate(subscription, plan, realPriceList, effectiveDate);
+ ApiEventCreate creationEvent = new ApiEventCreate(new ApiEventBuilder()
+ .setSubscriptionId(subscription.getId())
+ .setEventPlan(plan.getName())
+ .setEventPlanPhase(currentTimedPhase.getPhase().getName())
+ .setEventPriceList(realPriceList)
+ .setActiveVersion(subscription.getActiveVersion())
+ .setProcessedDate(processedDate)
+ .setEffectiveDate(effectiveDate)
+ .setRequestedDate(requestedDate));
+
+ TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnCreate(subscription, plan, realPriceList, effectiveDate);
+ PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(nextTimedPhase, subscription, processedDate);
+ List<EntitlementEvent> events = new ArrayList<EntitlementEvent>();
+ events.add(creationEvent);
+ if (nextPhaseEvent != null) {
+ events.add(nextPhaseEvent);
+ }
+ dao.createSubscription(subscription, events);
+ subscription.rebuildTransitions(events, catalogService.getCatalog());
+ return subscription;
+ }
+
+ public void cancel(SubscriptionData subscription, DateTime requestedDate, boolean eot)
+ throws EntitlementUserApiException {
+
+ SubscriptionState currentState = subscription.getState();
+ if (currentState != SubscriptionState.ACTIVE) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_CANCEL_BAD_STATE, subscription.getId(), currentState);
+ }
+
+ DateTime now = clock.getUTCNow();
+ requestedDate = (requestedDate != null) ? DefaultClock.truncateMs(requestedDate) : null;
+ if (requestedDate != null && requestedDate.isAfter(now)) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_INVALID_REQUESTED_DATE, requestedDate.toString());
+ }
+
+ IPlan currentPlan = subscription.getCurrentPlan();
+ PlanPhaseSpecifier planPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
+ currentPlan.getProduct().getCategory(),
+ subscription.getCurrentPlan().getBillingPeriod(),
+ subscription.getCurrentPriceList(),
+ subscription.getCurrentPhase().getPhaseType());
+
+ //TODO: Correctly handle exception
+ ActionPolicy policy = null;
+ try {
+ policy = catalogService.getCatalog().planCancelPolicy(planPhase);
+ } catch (CatalogApiException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, now);
+
+ EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
+ .setSubscriptionId(subscription.getId())
+ .setActiveVersion(subscription.getActiveVersion())
+ .setProcessedDate(now)
+ .setEffectiveDate(effectiveDate)
+ .setRequestedDate(now));
+
+ dao.cancelSubscription(subscription.getId(), cancelEvent);
+ subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getCatalog());
+ }
+
+
+ public void uncancel(SubscriptionData subscription) throws EntitlementUserApiException {
+ if (!subscription.isSubscriptionFutureCancelled()) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_UNCANCEL_BAD_STATE, subscription.getId().toString());
+ }
+ DateTime now = clock.getUTCNow();
+ EntitlementEvent uncancelEvent = new ApiEventUncancel(new ApiEventBuilder()
+ .setSubscriptionId(subscription.getId())
+ .setActiveVersion(subscription.getActiveVersion())
+ .setProcessedDate(now)
+ .setRequestedDate(now)
+ .setEffectiveDate(now));
+
+ List<EntitlementEvent> uncancelEvents = new ArrayList<EntitlementEvent>();
+ uncancelEvents.add(uncancelEvent);
+
+ DateTime planStartDate = subscription.getCurrentPlanStart();
+ TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, subscription.getCurrentPlan(), now, planStartDate);
+ PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(nextTimedPhase, subscription, now);
+ if (nextPhaseEvent != null) {
+ uncancelEvents.add(nextPhaseEvent);
+ }
+ dao.uncancelSubscription(subscription.getId(), uncancelEvents);
+ subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getCatalog());
+ }
+
+ public void changePlan(SubscriptionData subscription, String productName, BillingPeriod term,
+ String priceList, DateTime requestedDate) throws EntitlementUserApiException {
+
+ requestedDate = (requestedDate != null) ? DefaultClock.truncateMs(requestedDate) : null;
+ String currentPriceList = subscription.getCurrentPriceList();
+
+ SubscriptionState currentState = subscription.getState();
+ if (currentState != SubscriptionState.ACTIVE) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_CHANGE_NON_ACTIVE, subscription.getId(), currentState);
+ }
+
+ if (subscription.isSubscriptionFutureCancelled()) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_CHANGE_FUTURE_CANCELLED, subscription.getId());
+ }
+
+ DateTime now = clock.getUTCNow();
+ PlanChangeResult planChangeResult = null;
+ try {
+
+ IProduct destProduct = catalogService.getCatalog().findProduct(productName);
+ // STEPH really catalog exception
+ if (destProduct == null) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_BAD_CATALOG,
+ productName, term.toString(), "");
+ }
+
+ IPlan currentPlan = subscription.getCurrentPlan();
+ PlanPhaseSpecifier fromPlanPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
+ currentPlan.getProduct().getCategory(),
+ currentPlan.getBillingPeriod(),
+ currentPriceList, subscription.getCurrentPhase().getPhaseType());
+ PlanSpecifier toPlanPhase = new PlanSpecifier(productName,
+ destProduct.getCategory(),
+ term,
+ priceList);
+
+ planChangeResult = catalogService.getCatalog().planChange(fromPlanPhase, toPlanPhase);
+ } catch (CatalogApiException e) {
+ throw new EntitlementUserApiException(e);
+ }
+
+ ActionPolicy policy = planChangeResult.getPolicy();
+ IPriceList newPriceList = planChangeResult.getNewPriceList();
+
+ //TODO: Correctly handle exception
+ IPlan newPlan = null;
+ try {
+ newPlan = catalogService.getCatalog().findPlan(productName, term, newPriceList.getName());
+ } catch (CatalogApiException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ if (newPlan == null) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_BAD_CATALOG,
+ productName, term.toString(), newPriceList.getName());
+ }
+
+ DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, now);
+
+ TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), effectiveDate);
+
+ EntitlementEvent changeEvent = new ApiEventChange(new ApiEventBuilder()
+ .setSubscriptionId(subscription.getId())
+ .setEventPlan(newPlan.getName())
+ .setEventPlanPhase(currentTimedPhase.getPhase().getName())
+ .setEventPriceList(newPriceList.getName())
+ .setActiveVersion(subscription.getActiveVersion())
+ .setProcessedDate(now)
+ .setEffectiveDate(effectiveDate)
+ .setRequestedDate(now));
+
+ TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), effectiveDate);
+ PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(nextTimedPhase, subscription, now);
+ 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())) {
+ changeEvents.add(nextPhaseEvent);
+ }
+ changeEvents.add(changeEvent);
+ dao.changePlan(subscription.getId(), changeEvents);
+ subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getCatalog());
+ }
+
+
+
+
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBuilder.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBuilder.java
index dbc2a5e..88646c3 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBuilder.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBuilder.java
@@ -50,6 +50,7 @@ public class SubscriptionBuilder {
this.paidThroughDate = original.getPaidThroughDate();
}
+
public SubscriptionBuilder setId(UUID id) {
this.id = id;
return this;
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 1454e92..8b27f6c 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
@@ -33,40 +33,20 @@ import com.ning.billing.catalog.api.CatalogApiException;
import com.ning.billing.catalog.api.ICatalog;
import com.ning.billing.catalog.api.IPlan;
import com.ning.billing.catalog.api.IPlanPhase;
-import com.ning.billing.catalog.api.IPriceList;
-import com.ning.billing.catalog.api.IProduct;
-import com.ning.billing.catalog.api.PlanChangeResult;
-import com.ning.billing.catalog.api.PlanPhaseSpecifier;
-import com.ning.billing.catalog.api.PlanSpecifier;
import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.entitlement.alignment.PlanAligner;
-import com.ning.billing.entitlement.alignment.TimedPhase;
-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.ApiEventCreate;
import com.ning.billing.entitlement.events.user.ApiEventType;
-import com.ning.billing.entitlement.events.user.ApiEventUncancel;
import com.ning.billing.entitlement.events.user.ApiEvent;
import com.ning.billing.entitlement.exceptions.EntitlementError;
-import com.ning.billing.entitlement.glue.InjectorMagic;
-import com.ning.billing.util.clock.DefaultClock;
import com.ning.billing.util.clock.Clock;
public class SubscriptionData implements Subscription {
- //
- // Singletons used to perform API changes
- private final Clock clock;
- private final EntitlementDao dao;
- private final ICatalog catalog;
- private final PlanAligner planAligner;
+ private final Clock clock;
+ private final SubscriptionApiService apiService;
//
// Final subscription fields
//
@@ -90,12 +70,15 @@ public class SubscriptionData implements Subscription {
//
private List<SubscriptionTransitionData> transitions;
- public SubscriptionData(SubscriptionBuilder builder, boolean rebuildTransition) {
+ // Transient object never returned at the API
+ public SubscriptionData(SubscriptionBuilder builder) {
+ this(builder, null, null);
+ }
+
+ public SubscriptionData(SubscriptionBuilder builder, SubscriptionApiService apiService, Clock clock) {
super();
- this.clock = InjectorMagic.getClock();
- this.dao = InjectorMagic.getEntitlementDao();
- this.catalog = InjectorMagic.getCatlog();
- this.planAligner = InjectorMagic.getPlanAligner();
+ this.apiService = apiService;
+ this.clock = clock;
this.id = builder.getId();
this.bundleId = builder.getBundleId();
this.startDate = builder.getStartDate();
@@ -104,9 +87,6 @@ public class SubscriptionData implements Subscription {
this.activeVersion = builder.getActiveVersion();
this.chargedThroughDate = builder.getChargedThroughDate();
this.paidThroughDate = builder.getPaidThroughDate();
- if (rebuildTransition) {
- rebuildTransitions();
- }
}
@Override
@@ -160,156 +140,18 @@ public class SubscriptionData implements Subscription {
@Override
public void cancel(DateTime requestedDate, boolean eot) throws EntitlementUserApiException {
-
- SubscriptionState currentState = getState();
- if (currentState != SubscriptionState.ACTIVE) {
- throw new EntitlementUserApiException(ErrorCode.ENT_CANCEL_BAD_STATE, id, currentState);
- }
-
- DateTime now = clock.getUTCNow();
- requestedDate = (requestedDate != null) ? DefaultClock.truncateMs(requestedDate) : null;
- if (requestedDate != null && requestedDate.isAfter(now)) {
- throw new EntitlementUserApiException(ErrorCode.ENT_INVALID_REQUESTED_DATE, requestedDate.toString());
- }
-
- IPlan currentPlan = getCurrentPlan();
- PlanPhaseSpecifier planPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
- currentPlan.getProduct().getCategory(),
- getCurrentPlan().getBillingPeriod(),
- getCurrentPriceList(),
- getCurrentPhase().getPhaseType());
-
- //TODO: Correctly handle exception
- ActionPolicy policy = null;
- try {
- policy = catalog.planCancelPolicy(planPhase);
- } catch (CatalogApiException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- DateTime effectiveDate = getPlanChangeEffectiveDate(policy, now);
-
- EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
- .setSubscriptionId(id)
- .setActiveVersion(activeVersion)
- .setProcessedDate(now)
- .setEffectiveDate(effectiveDate)
- .setRequestedDate(now));
-
- dao.cancelSubscription(id, cancelEvent);
- rebuildTransitions();
+ apiService.cancel(this, requestedDate, eot);
}
@Override
public void uncancel() throws EntitlementUserApiException {
- if (!isSubscriptionFutureCancelled()) {
- throw new EntitlementUserApiException(ErrorCode.ENT_UNCANCEL_BAD_STATE, id.toString());
- }
- DateTime now = clock.getUTCNow();
- EntitlementEvent uncancelEvent = new ApiEventUncancel(new ApiEventBuilder()
- .setSubscriptionId(id)
- .setActiveVersion(activeVersion)
- .setProcessedDate(now)
- .setRequestedDate(now)
- .setEffectiveDate(now));
-
- List<EntitlementEvent> uncancelEvents = new ArrayList<EntitlementEvent>();
- uncancelEvents.add(uncancelEvent);
-
- DateTime planStartDate = getCurrentPlanStart();
- TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(this, getCurrentPlan(), now, planStartDate);
- PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(nextTimedPhase, this, now);
- if (nextPhaseEvent != null) {
- uncancelEvents.add(nextPhaseEvent);
- }
- dao.uncancelSubscription(id, uncancelEvents);
- rebuildTransitions();
+ apiService.uncancel(this);
}
@Override
public void changePlan(String productName, BillingPeriod term,
String priceList, DateTime requestedDate) throws EntitlementUserApiException {
-
- requestedDate = (requestedDate != null) ? DefaultClock.truncateMs(requestedDate) : null;
- String currentPriceList = getCurrentPriceList();
-
- SubscriptionState currentState = getState();
- if (currentState != SubscriptionState.ACTIVE) {
- throw new EntitlementUserApiException(ErrorCode.ENT_CHANGE_NON_ACTIVE, id, currentState);
- }
-
- if (isSubscriptionFutureCancelled()) {
- throw new EntitlementUserApiException(ErrorCode.ENT_CHANGE_FUTURE_CANCELLED, id);
- }
-
- DateTime now = clock.getUTCNow();
- PlanChangeResult planChangeResult = null;
- try {
-
- IProduct destProduct = catalog.findProduct(productName);
- // STEPH really catalog exception
- if (destProduct == null) {
- throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_BAD_CATALOG,
- productName, term.toString(), "");
- }
-
- IPlan currentPlan = getCurrentPlan();
- PlanPhaseSpecifier fromPlanPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
- currentPlan.getProduct().getCategory(),
- currentPlan.getBillingPeriod(),
- currentPriceList, getCurrentPhase().getPhaseType());
- PlanSpecifier toPlanPhase = new PlanSpecifier(productName,
- destProduct.getCategory(),
- term,
- priceList);
-
- planChangeResult = catalog.planChange(fromPlanPhase, toPlanPhase);
- } catch (CatalogApiException e) {
- throw new EntitlementUserApiException(e);
- }
-
- ActionPolicy policy = planChangeResult.getPolicy();
- IPriceList newPriceList = planChangeResult.getNewPriceList();
-
- //TODO: Correctly handle exception
- IPlan newPlan = null;
- try {
- newPlan = catalog.findPlan(productName, term, newPriceList.getName());
- } catch (CatalogApiException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- if (newPlan == null) {
- throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_BAD_CATALOG,
- productName, term.toString(), newPriceList.getName());
- }
-
- DateTime effectiveDate = getPlanChangeEffectiveDate(policy, now);
-
- TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnChange(this, newPlan, newPriceList.getName(), effectiveDate);
-
- EntitlementEvent changeEvent = new ApiEventChange(new ApiEventBuilder()
- .setSubscriptionId(id)
- .setEventPlan(newPlan.getName())
- .setEventPlanPhase(currentTimedPhase.getPhase().getName())
- .setEventPriceList(newPriceList.getName())
- .setActiveVersion(activeVersion)
- .setProcessedDate(now)
- .setEffectiveDate(effectiveDate)
- .setRequestedDate(now));
-
- TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(this, newPlan, newPriceList.getName(), effectiveDate);
- PhaseEvent nextPhaseEvent = PhaseEventData.getNextPhaseEvent(nextTimedPhase, this, now);
- 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())) {
- changeEvents.add(nextPhaseEvent);
- }
- changeEvents.add(changeEvent);
- dao.changePlan(id, changeEvents);
- rebuildTransitions();
+ apiService.changePlan(this, productName, term, priceList, requestedDate);
}
@Override
@@ -407,7 +249,7 @@ public class SubscriptionData implements Subscription {
return activeTransitions;
}
- private boolean isSubscriptionFutureCancelled() {
+ public boolean isSubscriptionFutureCancelled() {
if (transitions == null) {
return false;
}
@@ -424,7 +266,7 @@ public class SubscriptionData implements Subscription {
}
- private DateTime getPlanChangeEffectiveDate(ActionPolicy policy, DateTime now) {
+ public DateTime getPlanChangeEffectiveDate(ActionPolicy policy, DateTime now) {
if (policy == ActionPolicy.IMMEDIATE) {
return now;
@@ -444,7 +286,7 @@ public class SubscriptionData implements Subscription {
}
- private DateTime getCurrentPhaseStart() {
+ public DateTime getCurrentPhaseStart() {
if (transitions == null) {
throw new EntitlementError(String.format("No transitions for subscription %s", getId()));
@@ -465,9 +307,8 @@ public class SubscriptionData implements Subscription {
return transitions.get(0).getEffectiveTransitionTime();
}
- private void rebuildTransitions() {
+ public void rebuildTransitions(final List<EntitlementEvent> events, final ICatalog catalog) {
- List<EntitlementEvent> events = dao.getEventsForSubscription(id);
if (events == null) {
return;
}
@@ -482,8 +323,7 @@ public class SubscriptionData implements Subscription {
String previousPhaseName = null;
String previousPriceList = null;
- this.transitions = new LinkedList<SubscriptionTransitionData>();
-
+ transitions = new LinkedList<SubscriptionTransitionData>();
for (final EntitlementEvent cur : events) {
if (!cur.isActive() || cur.getActiveVersion() < activeVersion) {
@@ -544,31 +384,42 @@ public class SubscriptionData implements Subscription {
IPlan nextPlan = null;
IPlanPhase nextPhase = null;
try {
- previousPlan = catalog.findPlan(previousPlanName);
+ previousPlan = catalog.findPlan(previousPlanName);
} catch (CatalogApiException e) {
- // TODO: handle exception
+ // TODO: handle exception
}
try {
- previousPhase = catalog.findPhase(previousPhaseName);
+ previousPhase = catalog.findPhase(previousPhaseName);
} catch (CatalogApiException e) {
// TODO: handle exception
- }
+ }
try {
- nextPlan = catalog.findPlan(nextPlanName);
+ nextPlan = catalog.findPlan(nextPlanName);
} catch (CatalogApiException e) {
- // TODO: handle exception
+ // TODO: handle exception
}
try {
- nextPhase = catalog.findPhase(nextPhaseName);
+ nextPhase = catalog.findPhase(nextPhaseName);
} catch (CatalogApiException e) {
- // TODO: handle exception
+ // TODO: handle exception
}
SubscriptionTransitionData transition =
- new SubscriptionTransitionData(cur.getId(), id, bundleId, cur.getType(), apiEventType,
- cur.getRequestedDate(), cur.getEffectiveDate(),
- previousState, previousPlan, previousPhase, previousPriceList,
- nextState, nextPlan, nextPhase, nextPriceList);
+ new SubscriptionTransitionData(cur.getId(),
+ id,
+ bundleId,
+ cur.getType(),
+ apiEventType,
+ cur.getRequestedDate(),
+ cur.getEffectiveDate(),
+ previousState,
+ previousPlan,
+ previousPhase,
+ previousPriceList,
+ nextState,
+ nextPlan,
+ nextPhase,
+ nextPriceList);
transitions.add(transition);
previousState = nextState;
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 6bfccc3..835da51 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
@@ -61,7 +61,7 @@ public interface EntitlementDao {
public void clearEventsReady(UUID ownerId, Collection<EntitlementEvent> cleared);
// Subscription creation, cancellation, changePlan apis
- public Subscription createSubscription(SubscriptionData subscription, List<EntitlementEvent> initialEvents);
+ public void createSubscription(SubscriptionData subscription, List<EntitlementEvent> initialEvents);
public void cancelSubscription(UUID subscriptionId, EntitlementEvent cancelEvent);
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 88410a2..7f80704 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
@@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
-import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
@@ -30,11 +29,12 @@ import org.skife.jdbi.v2.TransactionStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.collect.Lists;
import com.google.inject.Inject;
+import com.ning.billing.catalog.api.ICatalogService;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.config.EntitlementConfig;
import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionApiService;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionData;
import com.ning.billing.entitlement.api.user.SubscriptionBuilder;
@@ -51,6 +51,7 @@ public class EntitlementSqlDao implements EntitlementDao {
private final static Logger log = LoggerFactory.getLogger(EntitlementSqlDao.class);
+ private final SubscriptionApiService apiService;
private final Clock clock;
private final SubscriptionSqlDao subscriptionsDao;
private final BundleSqlDao bundlesDao;
@@ -59,9 +60,10 @@ public class EntitlementSqlDao implements EntitlementDao {
private final String hostname;
@Inject
- public EntitlementSqlDao(DBI dbi, Clock clock, EntitlementConfig config) {
+ public EntitlementSqlDao(DBI dbi, Clock clock, EntitlementConfig config, SubscriptionApiService apiService) {
this.clock = clock;
this.config = config;
+ this.apiService = apiService;
this.subscriptionsDao = dbi.onDemand(SubscriptionSqlDao.class);
this.eventsDao = dbi.onDemand(EventSqlDao.class);
this.bundlesDao = dbi.onDemand(BundleSqlDao.class);
@@ -87,7 +89,7 @@ public class EntitlementSqlDao implements EntitlementDao {
@Override
public Subscription getSubscriptionFromId(UUID subscriptionId) {
- return subscriptionsDao.getSubscriptionFromId(subscriptionId.toString());
+ return buildSubscription(subscriptionsDao.getSubscriptionFromId(subscriptionId.toString()));
}
@Override
@@ -96,7 +98,7 @@ public class EntitlementSqlDao implements EntitlementDao {
List<Subscription> subscriptions = subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString());
for (Subscription cur : subscriptions) {
if (((SubscriptionData)cur).getCategory() == ProductCategory.BASE) {
- return cur;
+ return buildSubscription(cur);
}
}
return null;
@@ -104,7 +106,7 @@ public class EntitlementSqlDao implements EntitlementDao {
@Override
public List<Subscription> getSubscriptions(UUID bundleId) {
- return subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString());
+ return buildSubscription(subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString()));
}
@Override
@@ -113,7 +115,7 @@ public class EntitlementSqlDao implements EntitlementDao {
if (bundle == null) {
return Collections.emptyList();
}
- return subscriptionsDao.getSubscriptionsFromBundleId(bundle.getId().toString());
+ return buildSubscription(subscriptionsDao.getSubscriptionsFromBundleId(bundle.getId().toString()));
}
@Override
@@ -208,7 +210,7 @@ public class EntitlementSqlDao implements EntitlementDao {
}
@Override
- public Subscription createSubscription(final SubscriptionData subscription,
+ public void createSubscription(final SubscriptionData subscription,
final List<EntitlementEvent> initialEvents) {
subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
@@ -226,7 +228,6 @@ public class EntitlementSqlDao implements EntitlementDao {
return null;
}
});
- return new SubscriptionData(new SubscriptionBuilder(subscription), true);
}
@Override
@@ -322,4 +323,21 @@ public class EntitlementSqlDao implements EntitlementDao {
dao.unactiveEvent(futureEventId.toString(), now);
}
}
+
+ private Subscription buildSubscription(Subscription input) {
+ if (input == null) {
+ return null;
+ }
+ return buildSubscription(Collections.singletonList(input)).get(0);
+ }
+
+ private List<Subscription> buildSubscription(List<Subscription> input) {
+ List<Subscription> result = new ArrayList<Subscription>(input.size());
+ for (Subscription cur : input) {
+ List<EntitlementEvent> events = eventsDao.getEventsForSubscription(cur.getId().toString());
+ Subscription reloaded = apiService.createFromExisting(new SubscriptionBuilder((SubscriptionData) cur), events);
+ result.add(reloaded);
+ }
+ return result;
+ }
}
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 94d8544..79c9f49 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
@@ -108,8 +108,7 @@ public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, C
.setStartDate(startDate)
.setActiveVersion(activeVersion)
.setChargedThroughDate(ctd)
- .setPaidThroughDate(ptd),
- true);
+ .setPaidThroughDate(ptd));
return subscription;
}
}
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 334de2d..4284181 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
@@ -28,6 +28,7 @@ import com.ning.billing.entitlement.api.test.DefaultEntitlementTestApi;
import com.ning.billing.entitlement.api.test.EntitlementTestApi;
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.engine.core.DefaultApiEventProcessor;
import com.ning.billing.entitlement.engine.core.Engine;
import com.ning.billing.entitlement.engine.core.EventNotifier;
@@ -59,6 +60,7 @@ public class EntitlementModule extends AbstractModule {
}
protected void installEntitlementCore() {
+ bind(SubscriptionApiService.class).asEagerSingleton();
bind(EntitlementService.class).to(Engine.class).asEagerSingleton();
bind(Engine.class).asEagerSingleton();
bind(PlanAligner.class).asEagerSingleton();
@@ -67,13 +69,8 @@ public class EntitlementModule extends AbstractModule {
bind(EntitlementBillingApi.class).to(DefaultEntitlementBillingApi.class).asEagerSingleton();
}
- protected void installInjectorMagic() {
- bind(InjectorMagic.class).asEagerSingleton();
- }
-
@Override
protected void configure() {
- installInjectorMagic();
installConfig();
installClock();
installApiEventProcessor();
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
index 477075b..80e4904 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
@@ -32,7 +32,6 @@ import com.ning.billing.entitlement.events.EntitlementEvent;
import com.ning.billing.entitlement.events.phase.PhaseEvent;
import com.ning.billing.entitlement.events.user.ApiEventType;
import com.ning.billing.entitlement.events.user.ApiEvent;
-import com.ning.billing.entitlement.glue.InjectorMagic;
import com.ning.billing.lifecycle.KillbillService.ServiceException;
import com.ning.billing.util.clock.ClockMock;
import com.ning.billing.util.clock.Clock;
@@ -50,7 +49,6 @@ import org.testng.annotations.BeforeMethod;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
-import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -92,7 +90,6 @@ public abstract class TestUserApiBase {
@AfterClass(groups={"setup"})
public void tearDown() {
try {
- InjectorMagic.instance = null;
busService.getEventBus().register(testListener);
((DefaultEventBusService) busService).stopBus();
} catch (Exception e) {
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 c9e1385..c799dfc 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
@@ -30,10 +30,13 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
+import com.ning.billing.catalog.api.ICatalog;
+import com.ning.billing.catalog.api.ICatalogService;
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.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionApiService;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionData;
import com.ning.billing.entitlement.api.user.SubscriptionBuilder;
@@ -54,14 +57,16 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
private final TreeSet<EntitlementEvent> events;
private final Clock clock;
private final EntitlementConfig config;
+ private final SubscriptionApiService apiService;
@Inject
- public MockEntitlementDaoMemory(Clock clock, EntitlementConfig config) {
+ public MockEntitlementDaoMemory(Clock clock, EntitlementConfig config, SubscriptionApiService apiService) {
super();
this.clock = clock;
this.config = config;
+ this.apiService = apiService;
this.bundles = new ArrayList<SubscriptionBundle>();
this.subscriptions = new ArrayList<Subscription>();
this.events = new TreeSet<EntitlementEvent>();
@@ -124,14 +129,13 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
@Override
- public Subscription createSubscription(SubscriptionData subscription, List<EntitlementEvent> initalEvents) {
+ public void createSubscription(SubscriptionData subscription, List<EntitlementEvent> initalEvents) {
synchronized(events) {
events.addAll(initalEvents);
}
Subscription updatedSubscription = buildSubscription(subscription);
subscriptions.add(updatedSubscription);
- return updatedSubscription;
}
@Override
@@ -228,7 +232,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
}
private Subscription buildSubscription(SubscriptionData in) {
- return new SubscriptionData(new SubscriptionBuilder(in), true);
+ return apiService.createFromExisting(new SubscriptionBuilder(in), getEventsForSubscription(in.getId()));
}
@Override
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 18896e9..118174b 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
@@ -24,7 +24,10 @@ 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.catalog.api.ICatalog;
+import com.ning.billing.catalog.api.ICatalogService;
import com.ning.billing.config.EntitlementConfig;
+import com.ning.billing.entitlement.api.user.SubscriptionApiService;
import com.ning.billing.util.clock.Clock;
public class MockEntitlementDaoSql extends EntitlementSqlDao implements MockEntitlementDao {
@@ -32,8 +35,8 @@ public class MockEntitlementDaoSql extends EntitlementSqlDao implements MockEnti
private final ResetSqlDao resetDao;
@Inject
- public MockEntitlementDaoSql(DBI dbi, Clock clock, EntitlementConfig config) {
- super(dbi, clock, config);
+ public MockEntitlementDaoSql(DBI dbi, Clock clock, EntitlementConfig config, SubscriptionApiService apiService ) {
+ super(dbi, clock, config, apiService);
this.resetDao = dbi.onDemand(ResetSqlDao.class);
}