killbill-memoizeit
Changes
entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionApi.java 14(+14 -0)
subscription/src/main/java/com/ning/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java 14(+2 -12)
subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java 47(+41 -6)
Details
diff --git a/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseInternalApi.java b/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseInternalApi.java
index b8de4a1..cdbb425 100644
--- a/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseInternalApi.java
+++ b/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseInternalApi.java
@@ -49,7 +49,7 @@ public interface SubscriptionBaseInternalApi {
public List<SubscriptionBaseBundle> getBundlesForKey(final String bundleKey, final InternalTenantContext context);
- public SubscriptionBaseBundle getActiveBundleForKey(final String bundleKey, final InternalTenantContext context) throws SubscriptionBaseApiException;
+ public List<UUID> getNonAOSubscriptionIdsForKey(final String bundleKey, final InternalTenantContext context);
public List<SubscriptionBase> getSubscriptionsForBundle(final UUID bundleId, final InternalTenantContext context);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
index a1b3deb..4481d3a 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
@@ -44,6 +44,7 @@ import com.ning.billing.entitlement.DefaultEntitlementService;
import com.ning.billing.entitlement.EntitlementService;
import com.ning.billing.entitlement.EntitlementTransitionType;
import com.ning.billing.entitlement.EventsStream;
+import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
import com.ning.billing.entitlement.block.BlockingChecker;
import com.ning.billing.entitlement.dao.BlockingStateDao;
import com.ning.billing.entitlement.engine.core.EntitlementNotificationKey;
@@ -79,8 +80,8 @@ public class DefaultEntitlementApi implements EntitlementApi {
public static final String ENT_STATE_CLEAR = "ENT_CLEAR";
public static final String ENT_STATE_CANCELLED = "ENT_CANCELLED";
- private final SubscriptionBaseInternalApi subscriptionInternalApi;
- private final SubscriptionBaseTransferApi subscriptionTransferApi;
+ private final SubscriptionBaseInternalApi subscriptionBaseInternalApi;
+ private final SubscriptionBaseTransferApi subscriptionBaseTransferApi;
private final AccountInternalApi accountApi;
private final Clock clock;
private final InternalCallContextFactory internalCallContextFactory;
@@ -100,8 +101,8 @@ public class DefaultEntitlementApi implements EntitlementApi {
final EventsStreamBuilder eventsStreamBuilder, final EntitlementUtils entitlementUtils) {
this.eventBus = eventBus;
this.internalCallContextFactory = internalCallContextFactory;
- this.subscriptionInternalApi = subscriptionInternalApi;
- this.subscriptionTransferApi = subscriptionTransferApi;
+ this.subscriptionBaseInternalApi = subscriptionInternalApi;
+ this.subscriptionBaseTransferApi = subscriptionTransferApi;
this.accountApi = accountApi;
this.clock = clock;
this.checker = checker;
@@ -116,14 +117,19 @@ public class DefaultEntitlementApi implements EntitlementApi {
public Entitlement createBaseEntitlement(final UUID accountId, final PlanPhaseSpecifier planPhaseSpecifier, final String externalKey, final LocalDate effectiveDate, final CallContext callContext) throws EntitlementApiException {
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(accountId, callContext);
try {
- final SubscriptionBaseBundle bundle = subscriptionInternalApi.createBundleForAccount(accountId, externalKey, contextWithValidAccountRecordId);
+
+ if (entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, contextWithValidAccountRecordId) != null) {
+ throw new EntitlementApiException(new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS, externalKey));
+ }
+
+ final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.createBundleForAccount(accountId, externalKey, contextWithValidAccountRecordId);
final DateTime referenceTime = clock.getUTCNow();
final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(effectiveDate, referenceTime, contextWithValidAccountRecordId);
- final SubscriptionBase subscription = subscriptionInternalApi.createSubscription(bundle.getId(), planPhaseSpecifier, requestedDate, contextWithValidAccountRecordId);
+ final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(bundle.getId(), planPhaseSpecifier, requestedDate, contextWithValidAccountRecordId);
return new DefaultEntitlement(subscription.getId(), eventsStreamBuilder, this,
- blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
+ blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
entitlementUtils, dateHelper, clock, internalCallContextFactory, callContext);
} catch (SubscriptionBaseApiException e) {
throw new EntitlementApiException(e);
@@ -148,10 +154,10 @@ public class DefaultEntitlementApi implements EntitlementApi {
try {
final InternalCallContext context = internalCallContextFactory.createInternalCallContext(callContext);
- final SubscriptionBase subscription = subscriptionInternalApi.createSubscription(bundleId, planPhaseSpecifier, requestedDate, context);
+ final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(bundleId, planPhaseSpecifier, requestedDate, context);
return new DefaultEntitlement(subscription.getId(), eventsStreamBuilder, this,
- blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
+ blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
entitlementUtils, dateHelper, clock, internalCallContextFactory, callContext);
} catch (SubscriptionBaseApiException e) {
throw new EntitlementApiException(e);
@@ -162,12 +168,12 @@ public class DefaultEntitlementApi implements EntitlementApi {
public List<EntitlementAOStatusDryRun> getDryRunStatusForChange(final UUID bundleId, final String targetProductName, final LocalDate effectiveDate, final TenantContext context) throws EntitlementApiException {
final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
try {
- final SubscriptionBaseBundle bundle = subscriptionInternalApi.getBundleFromId(bundleId, internalContext);
- final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundleId, internalContext);
+ final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getBundleFromId(bundleId, internalContext);
+ final SubscriptionBase baseSubscription = subscriptionBaseInternalApi.getBaseSubscription(bundleId, internalContext);
final InternalTenantContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(bundle.getAccountId(), context);
final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(effectiveDate, baseSubscription.getStartDate(), contextWithValidAccountRecordId);
- return subscriptionInternalApi.getDryRunChangePlanStatus(baseSubscription.getId(), targetProductName, requestedDate, contextWithValidAccountRecordId);
+ return subscriptionBaseInternalApi.getDryRunChangePlanStatus(baseSubscription.getId(), targetProductName, requestedDate, contextWithValidAccountRecordId);
} catch (SubscriptionBaseApiException e) {
throw new EntitlementApiException(e);
}
@@ -177,7 +183,7 @@ public class DefaultEntitlementApi implements EntitlementApi {
public Entitlement getEntitlementForId(final UUID uuid, final TenantContext tenantContext) throws EntitlementApiException {
final EventsStream eventsStream = eventsStreamBuilder.buildForEntitlement(uuid, tenantContext);
return new DefaultEntitlement(eventsStream, eventsStreamBuilder, this,
- blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
+ blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
entitlementUtils, dateHelper, clock, internalCallContextFactory);
}
@@ -186,7 +192,7 @@ public class DefaultEntitlementApi implements EntitlementApi {
final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(tenantContext);
final UUID accountId;
try {
- accountId = subscriptionInternalApi.getBundleFromId(bundleId, internalContext).getAccountId();
+ accountId = subscriptionBaseInternalApi.getBundleFromId(bundleId, internalContext).getAccountId();
} catch (SubscriptionBaseApiException e) {
throw new EntitlementApiException(e);
}
@@ -224,7 +230,7 @@ public class DefaultEntitlementApi implements EntitlementApi {
@Override
public Entitlement apply(final EventsStream eventsStream) {
return new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi,
- blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
+ blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
entitlementUtils, dateHelper, clock, internalCallContextFactory);
}
});
@@ -239,9 +245,9 @@ public class DefaultEntitlementApi implements EntitlementApi {
throw new EntitlementApiException(ErrorCode.ENT_ALREADY_BLOCKED, bundleId);
}
- final SubscriptionBaseBundle bundle = subscriptionInternalApi.getBundleFromId(bundleId, contextWithValidAccountRecordId);
+ final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getBundleFromId(bundleId, contextWithValidAccountRecordId);
final Account account = accountApi.getAccountById(bundle.getAccountId(), contextWithValidAccountRecordId);
- final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundleId, contextWithValidAccountRecordId);
+ final SubscriptionBase baseSubscription = subscriptionBaseInternalApi.getBaseSubscription(bundleId, contextWithValidAccountRecordId);
final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(localEffectiveDate, baseSubscription.getStartDate(), contextWithValidAccountRecordId);
if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, account.getTimeZone())) {
@@ -276,9 +282,9 @@ public class DefaultEntitlementApi implements EntitlementApi {
public void resume(final UUID bundleId, final LocalDate localEffectiveDate, final CallContext context) throws EntitlementApiException {
try {
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, context);
- final SubscriptionBaseBundle bundle = subscriptionInternalApi.getBundleFromId(bundleId, contextWithValidAccountRecordId);
+ final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getBundleFromId(bundleId, contextWithValidAccountRecordId);
final Account account = accountApi.getAccountById(bundle.getAccountId(), contextWithValidAccountRecordId);
- final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundleId, contextWithValidAccountRecordId);
+ final SubscriptionBase baseSubscription = subscriptionBaseInternalApi.getBaseSubscription(bundleId, contextWithValidAccountRecordId);
final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(localEffectiveDate, baseSubscription.getStartDate(), contextWithValidAccountRecordId);
@@ -338,18 +344,23 @@ public class DefaultEntitlementApi implements EntitlementApi {
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(sourceAccountId, context);
try {
- final SubscriptionBaseBundle bundle = subscriptionInternalApi.getActiveBundleForKey(externalKey, contextWithValidAccountRecordId);
- if (!bundle.getAccountId().equals(sourceAccountId)) {
+
+ final UUID activeSubscriptionIdForKey = entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, contextWithValidAccountRecordId);
+ final SubscriptionBase baseSubscription = activeSubscriptionIdForKey != null ?
+ subscriptionBaseInternalApi.getSubscriptionFromId(activeSubscriptionIdForKey, contextWithValidAccountRecordId) : null;
+ final SubscriptionBaseBundle baseBundle = baseSubscription != null ?
+ subscriptionBaseInternalApi.getBundleFromId(baseSubscription.getBundleId(), contextWithValidAccountRecordId) : null;
+
+ if (baseBundle == null || ! baseBundle.getAccountId().equals(sourceAccountId)) {
throw new EntitlementApiException(new SubscriptionBaseApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_KEY, externalKey));
}
- final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundle.getId(), contextWithValidAccountRecordId);
final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(effectiveDate, baseSubscription.getStartDate(), contextWithValidAccountRecordId);
- final SubscriptionBaseBundle newBundle = subscriptionTransferApi.transferBundle(sourceAccountId, destAccountId, externalKey, requestedDate, true, cancelImm, context);
+ final SubscriptionBaseBundle newBundle = subscriptionBaseTransferApi.transferBundle(sourceAccountId, destAccountId, externalKey, requestedDate, true, cancelImm, context);
// Block all associated subscriptions - TODO Do we want to block the bundle as well (this will add an extra STOP_ENTITLEMENT event in the bundle timeline stream)?
// Note that there is no un-transfer at the moment, so we effectively add a blocking state on disk for all subscriptions
- for (final SubscriptionBase subscriptionBase : subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), contextWithValidAccountRecordId)) {
+ for (final SubscriptionBase subscriptionBase : subscriptionBaseInternalApi.getSubscriptionsForBundle(baseBundle.getId(), contextWithValidAccountRecordId)) {
final BlockingState blockingState = new DefaultBlockingState(subscriptionBase.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, requestedDate);
entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingState, contextWithValidAccountRecordId);
}
@@ -378,4 +389,5 @@ public class DefaultEntitlementApi implements EntitlementApi {
throw new EntitlementApiException(e, ErrorCode.__UNKNOWN_ERROR_CODE);
}
}
+
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
index 253525e..928930a 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -33,6 +33,10 @@ import com.ning.billing.ObjectType;
import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.entitlement.AccountEntitlements;
import com.ning.billing.entitlement.EntitlementInternalApi;
+import com.ning.billing.entitlement.EventsStream;
+import com.ning.billing.entitlement.api.svcs.DefaultEntitlementInternalApi;
+import com.ning.billing.entitlement.engine.core.EntitlementUtils;
+import com.ning.billing.subscription.api.SubscriptionBase;
import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
@@ -70,18 +74,20 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
};
private final EntitlementInternalApi entitlementInternalApi;
- private final SubscriptionBaseInternalApi subscriptionInternalApi;
+ private final SubscriptionBaseInternalApi subscriptionBaseInternalApi;
private final InternalCallContextFactory internalCallContextFactory;
private final NonEntityDao nonEntityDao;
private final CacheControllerDispatcher cacheControllerDispatcher;
+ private final EntitlementUtils entitlementUtils;
@Inject
public DefaultSubscriptionApi(final EntitlementInternalApi entitlementInternalApi, final SubscriptionBaseInternalApi subscriptionInternalApi,
- final InternalCallContextFactory internalCallContextFactory, final NonEntityDao nonEntityDao, final CacheControllerDispatcher cacheControllerDispatcher) {
+ final InternalCallContextFactory internalCallContextFactory, final EntitlementUtils entitlementUtils, final NonEntityDao nonEntityDao, final CacheControllerDispatcher cacheControllerDispatcher) {
this.entitlementInternalApi = entitlementInternalApi;
- this.subscriptionInternalApi = subscriptionInternalApi;
+ this.subscriptionBaseInternalApi = subscriptionInternalApi;
this.internalCallContextFactory = internalCallContextFactory;
this.nonEntityDao = nonEntityDao;
+ this.entitlementUtils = entitlementUtils;
this.cacheControllerDispatcher = cacheControllerDispatcher;
}
@@ -144,8 +150,12 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
public SubscriptionBundle getActiveSubscriptionBundleForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
try {
- final SubscriptionBaseBundle baseBundle = subscriptionInternalApi.getActiveBundleForKey(externalKey, internalContext);
- return getSubscriptionBundle(baseBundle.getId(), context);
+ final UUID activeSubscriptionIdForKey = entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, internalContext);
+ if (activeSubscriptionIdForKey == null) {
+ throw new SubscriptionApiException(new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS, externalKey));
+ }
+ final SubscriptionBase subscriptionBase = subscriptionBaseInternalApi.getSubscriptionFromId(activeSubscriptionIdForKey, internalContext);
+ return getSubscriptionBundle(subscriptionBase.getBundleId(), context);
} catch (SubscriptionBaseApiException e) {
throw new SubscriptionApiException(e);
}
@@ -154,7 +164,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
@Override
public List<SubscriptionBundle> getSubscriptionBundlesForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
- final List<SubscriptionBaseBundle> baseBundles = subscriptionInternalApi.getBundlesForKey(externalKey, internalContext);
+ final List<SubscriptionBaseBundle> baseBundles = subscriptionBaseInternalApi.getBundlesForKey(externalKey, internalContext);
final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(baseBundles.size());
for (final SubscriptionBaseBundle cur : baseBundles) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EntitlementUtils.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EntitlementUtils.java
index c595673..461fec5 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EntitlementUtils.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EntitlementUtils.java
@@ -17,6 +17,7 @@
package com.ning.billing.entitlement.engine.core;
import java.io.IOException;
+import java.util.List;
import java.util.UUID;
import javax.inject.Inject;
@@ -29,12 +30,17 @@ import com.ning.billing.bus.api.BusEvent;
import com.ning.billing.bus.api.PersistentBus;
import com.ning.billing.bus.api.PersistentBus.EventBusException;
import com.ning.billing.callcontext.InternalCallContext;
+import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.clock.Clock;
import com.ning.billing.entitlement.DefaultEntitlementService;
+import com.ning.billing.entitlement.EventsStream;
import com.ning.billing.entitlement.api.BlockingApiException;
import com.ning.billing.entitlement.api.BlockingState;
import com.ning.billing.entitlement.api.BlockingStateType;
import com.ning.billing.entitlement.api.DefaultBlockingTransitionInternalEvent;
+import com.ning.billing.entitlement.api.DefaultEntitlementApi;
+import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
+import com.ning.billing.entitlement.api.EntitlementApiException;
import com.ning.billing.entitlement.block.BlockingChecker;
import com.ning.billing.entitlement.block.BlockingChecker.BlockingAggregator;
import com.ning.billing.entitlement.dao.BlockingStateDao;
@@ -42,6 +48,10 @@ import com.ning.billing.notificationq.api.NotificationEvent;
import com.ning.billing.notificationq.api.NotificationQueue;
import com.ning.billing.notificationq.api.NotificationQueueService;
import com.ning.billing.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
+import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
public class EntitlementUtils {
@@ -49,6 +59,7 @@ public class EntitlementUtils {
private final BlockingStateDao dao;
private final BlockingChecker blockingChecker;
+ private final SubscriptionBaseInternalApi subscriptionBaseInternalApi;
private final PersistentBus eventBus;
private final Clock clock;
protected final NotificationQueueService notificationQueueService;
@@ -56,11 +67,13 @@ public class EntitlementUtils {
@Inject
public EntitlementUtils(final BlockingStateDao dao, final BlockingChecker blockingChecker,
final PersistentBus eventBus, final Clock clock,
+ final SubscriptionBaseInternalApi subscriptionBaseInternalApi,
final NotificationQueueService notificationQueueService) {
this.dao = dao;
this.blockingChecker = blockingChecker;
this.eventBus = eventBus;
this.clock = clock;
+ this.subscriptionBaseInternalApi = subscriptionBaseInternalApi;
this.notificationQueueService = notificationQueueService;
}
@@ -81,6 +94,26 @@ public class EntitlementUtils {
}
}
+ /**
+ *
+ * @param externalKey the bundle externalKey
+ * @param tenantContext the context
+ * @return the id of the first subscription (BASE or STANDALONE) that is still active for that key
+ */
+ public UUID getFirstActiveSubscriptionIdForKeyOrNull(final String externalKey, final InternalTenantContext tenantContext) {
+
+ final List<UUID> nonAddonUUIDs = subscriptionBaseInternalApi.getNonAOSubscriptionIdsForKey(externalKey, tenantContext);
+ for (final UUID cur : nonAddonUUIDs) {
+ final BlockingState state = dao.getBlockingStateForService(cur, BlockingStateType.SUBSCRIPTION, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME, tenantContext);
+ if (state == null || !state.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED)) {
+ return cur;
+ }
+ }
+ return null;
+ }
+
+
+
private BlockingAggregator getBlockingStateFor(final UUID blockableId, final BlockingStateType type, final InternalCallContext context) {
try {
return blockingChecker.getBlockedStatus(blockableId, type, context);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionApi.java
index bcfd0f1..6856ec9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionApi.java
@@ -23,6 +23,7 @@ import org.joda.time.LocalDate;
import org.testng.Assert;
import org.testng.annotations.Test;
+import com.ning.billing.ErrorCode;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.api.TestApiListener.NextEvent;
@@ -37,6 +38,8 @@ import com.ning.billing.util.audit.AuditLog;
import com.ning.billing.util.audit.ChangeType;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbeddedDB {
@@ -106,12 +109,23 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
final List<SubscriptionBundle> bundles = subscriptionApi.getSubscriptionBundlesForExternalKey(externalKey, callContext);
assertEquals(bundles.size(), 1);
+ final SubscriptionBundle activeBundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey(externalKey, callContext);
+ assertEquals(activeBundle.getId(), entitlement.getBundleId());
+
// Cancel entitlement
clock.addDays(3);
testListener.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
entitlement.cancelEntitlementWithDate(new LocalDate(clock.getUTCNow(), account.getTimeZone()), true, callContext);
assertListenerStatus();
+ try {
+ subscriptionApi.getActiveSubscriptionBundleForExternalKey(externalKey, callContext);
+ Assert.fail("Expected getActiveSubscriptionBundleForExternalKey to fail after cancellation");
+ } catch (SubscriptionApiException e) {
+ assertEquals(e.getCode(), ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS.getCode());
+
+ }
+
clock.addDays(1);
// Re-create a new bundle with same externalKey
final PlanPhaseSpecifier spec2 = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
diff --git a/subscription/src/main/java/com/ning/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/com/ning/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index 161268e..ea1bc70 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -163,10 +163,6 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
public SubscriptionBaseBundle createBundleForAccount(final UUID accountId, final String bundleKey, final InternalCallContext context) throws SubscriptionBaseApiException {
final List<SubscriptionBaseBundle> existingBundles = dao.getSubscriptionBundlesForKey(bundleKey, context);
- final SubscriptionBaseBundle result = getActiveBundleForKeyNotException(existingBundles, dao, clock, context);
- if (result != null) {
- throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS, bundleKey);
- }
final DateTime now = clock.getUTCNow();
final DateTime originalCreatedDate = existingBundles.size() > 0 ? existingBundles.get(0).getCreatedDate() : now;
final DefaultSubscriptionBaseBundle bundle = new DefaultSubscriptionBaseBundle(bundleKey, accountId, now, originalCreatedDate, now, now);
@@ -191,14 +187,8 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
}
@Override
- public SubscriptionBaseBundle getActiveBundleForKey(final String bundleKey, final InternalTenantContext context) throws SubscriptionBaseApiException {
- final List<SubscriptionBaseBundle> existingBundles = dao.getSubscriptionBundlesForKey(bundleKey, context);
-
- final SubscriptionBaseBundle result = getActiveBundleForKeyNotException(existingBundles, dao, clock, context);
- if (result == null) {
- throw new SubscriptionBaseApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_KEY, bundleKey);
- }
- return result;
+ public List<UUID> getNonAOSubscriptionIdsForKey(final String bundleKey, final InternalTenantContext context) {
+ return dao.getNonAOSubscriptionIdsForKey(bundleKey, context);
}
public static SubscriptionBaseBundle getActiveBundleForKeyNotException(final List<SubscriptionBaseBundle> existingBundles, final SubscriptionDao dao, final Clock clock, final InternalTenantContext context) {
diff --git a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 21edeeb..c4e0a82 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -93,8 +93,9 @@ import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Collections2;
-import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
public class DefaultSubscriptionDao implements SubscriptionDao {
@@ -181,6 +182,43 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
}
@Override
+ public List<UUID> getNonAOSubscriptionIdsForKey(final String bundleKey, final InternalTenantContext context) {
+ return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<UUID>>() {
+ @Override
+ public List<UUID> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+
+ final BundleSqlDao bundleSqlDao = entitySqlDaoWrapperFactory.become(BundleSqlDao.class);
+
+ final List<SubscriptionBundleModelDao> bundles = bundleSqlDao.getBundlesForKey(bundleKey, context);
+
+ final List<UUID> result = new ArrayList<UUID>();
+ final SubscriptionSqlDao subscriptionSqlDao = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class);
+ for (SubscriptionBundleModelDao cur : bundles) {
+ final List<SubscriptionModelDao> subscriptions = subscriptionSqlDao.getSubscriptionsFromBundleId(cur.getId().toString(), context);
+
+ final Iterable nonAddonSubscriptions = Iterables.filter(subscriptions, new Predicate<SubscriptionModelDao>() {
+ @Override
+ public boolean apply(final SubscriptionModelDao input) {
+ return input.getCategory() != ProductCategory.ADD_ON;
+ }
+ });
+ if (nonAddonSubscriptions.iterator().hasNext()) {
+
+ final Iterable nonAddonSubscriptionIds = Iterables.transform(nonAddonSubscriptions, new Function<SubscriptionModelDao, UUID>() {
+ @Override
+ public UUID apply(@Nullable final SubscriptionModelDao input) {
+ return input.getId();
+ }
+ });
+ result.addAll(Lists.newArrayList(nonAddonSubscriptionIds));
+ }
+ }
+ return result;
+ }
+ });
+ }
+
+ @Override
public SubscriptionBaseBundle createSubscriptionBundle(final DefaultSubscriptionBaseBundle bundle, final InternalCallContext context) {
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<SubscriptionBaseBundle>() {
@Override
@@ -259,7 +297,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
});
}
-
@Override
public Map<UUID, List<SubscriptionBase>> getSubscriptionsForAccount(final InternalTenantContext context) {
final Map<UUID, List<SubscriptionBase>> subscriptionsFromAccountId = getSubscriptionsFromAccountId(context);
@@ -279,7 +316,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
final Multimap<UUID, SubscriptionBaseEvent> eventsForSubscriptions = ArrayListMultimap.create();
for (final SubscriptionBase cur : subscriptionsForBundle) {
- final Collection<SubscriptionBaseEvent> events= Collections2.filter(eventsForAccount, new Predicate<SubscriptionBaseEvent>() {
+ final Collection<SubscriptionBaseEvent> events = Collections2.filter(eventsForAccount, new Predicate<SubscriptionBaseEvent>() {
@Override
public boolean apply(final SubscriptionBaseEvent input) {
return input.getSubscriptionId().equals(cur.getId());
@@ -289,7 +326,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
eventsForSubscriptions.putAll(cur.getId(), ImmutableList.copyOf(events));
}
- result.put(bundleId, buildBundleSubscriptions(subscriptionsForBundle, eventsForSubscriptions,context));
+ result.put(bundleId, buildBundleSubscriptions(subscriptionsForBundle, eventsForSubscriptions, context));
}
return result;
}
@@ -379,7 +416,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
});
}
-
@Override
public Map<UUID, List<SubscriptionBaseEvent>> getEventsForBundle(final UUID bundleId, final InternalTenantContext context) {
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Map<UUID, List<SubscriptionBaseEvent>>>() {
@@ -800,7 +836,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
final List<SubscriptionBase> result = new ArrayList<SubscriptionBase>(input.size());
for (final SubscriptionBase cur : input) {
-
final List<SubscriptionBaseEvent> events = eventsForSubscription != null ?
(List<SubscriptionBaseEvent>) eventsForSubscription.get(cur.getId()) :
getEventsForSubscription(cur.getId(), context);
diff --git a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/RepairSubscriptionDao.java b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/RepairSubscriptionDao.java
index b05e650..fe12bc3 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/RepairSubscriptionDao.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/RepairSubscriptionDao.java
@@ -306,6 +306,11 @@ public class RepairSubscriptionDao implements SubscriptionDao, RepairSubscriptio
}
@Override
+ public List<UUID> getNonAOSubscriptionIdsForKey(final String bundleKey, final InternalTenantContext context) {
+ throw new SubscriptionBaseError(NOT_IMPLEMENTED);
+ }
+
+ @Override
public List<SubscriptionBaseBundle> getSubscriptionBundlesForAccountAndKey(final UUID accountId, final String bundleKey, final InternalTenantContext context) {
throw new SubscriptionBaseError(NOT_IMPLEMENTED);
}
diff --git a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/SubscriptionDao.java b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/SubscriptionDao.java
index b7cffc0..4b4c608 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/SubscriptionDao.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/SubscriptionDao.java
@@ -39,6 +39,8 @@ public interface SubscriptionDao {
public List<SubscriptionBaseBundle> getSubscriptionBundlesForKey(String bundleKey, InternalTenantContext context);
+ public List<UUID> getNonAOSubscriptionIdsForKey(final String bundleKey, final InternalTenantContext context);
+
public List<SubscriptionBaseBundle> getSubscriptionBundlesForAccountAndKey(UUID accountId, String bundleKey, InternalTenantContext context);
public SubscriptionBaseBundle getSubscriptionBundleFromId(UUID bundleId, InternalTenantContext context);
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java b/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java
index 62cf563..8b5af59 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java
@@ -99,10 +99,6 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
// The MIGRATE_BILLING event should have been invalidated
assertEquals(subscriptionInternalApi.getBillingTransitions(oldBaseSubscription, internalCallContext).size(), 0);
//assertEquals(subscriptionInternalApi.getBillingTransitions(oldBaseSubscription, internalCallContext).get(0).getTransitionType(), SubscriptionBaseTransitionType.CANCEL);
-
- final SubscriptionBaseBundle newBundle = subscriptionInternalApi.getActiveBundleForKey(bundle.getExternalKey(), internalCallContext);
- assertNotEquals(newBundle.getId(), bundle.getId());
- assertEquals(newBundle.getOriginalCreatedDate().compareTo(bundleCreatedDate), 0);
}
@Test(groups = "slow")
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCreate.java b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCreate.java
index de6eef8..ebb176c 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCreate.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCreate.java
@@ -57,14 +57,6 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
assertListenerStatus();
assertNotNull(subscription);
- try {
- final SubscriptionBaseBundle newBundle = subscriptionInternalApi.createBundleForAccount(bundle.getAccountId(), DefaultSubscriptionTestInitializer.DEFAULT_BUNDLE_KEY, internalCallContext);
-
- Assert.fail("Unexpected success to create a bundle");
- } catch (SubscriptionBaseApiException e) {
- Assert.assertEquals(e.getCode(), ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS.getCode());
- }
-
testListener.pushExpectedEvent(NextEvent.CANCEL);
subscription.cancelWithDate(clock.getUTCNow(), callContext);
assertListenerStatus();
diff --git a/subscription/src/test/java/com/ning/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java b/subscription/src/test/java/com/ning/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
index 77ce836..4d23fd3 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
@@ -114,6 +114,11 @@ public class MockSubscriptionDaoMemory implements SubscriptionDao {
}
@Override
+ public List<UUID> getNonAOSubscriptionIdsForKey(final String bundleKey, final InternalTenantContext context) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public SubscriptionBaseBundle getSubscriptionBundleFromId(final UUID bundleId, final InternalTenantContext context) {
for (final SubscriptionBaseBundle cur : bundles) {
if (cur.getId().equals(bundleId)) {