diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
index 475527c..db21945 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
@@ -72,12 +72,12 @@ import org.killbill.clock.Clock;
import org.killbill.notificationq.api.NotificationQueueService;
import com.google.common.base.Function;
+import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
-import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logCreateEntitlement;
import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logCreateEntitlementsWithAOs;
import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logPauseResumeEntitlement;
import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logTransferEntitlement;
@@ -145,19 +145,6 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
return createdEntitlements.get(0);
}
- private BaseEntitlementWithAddOnsSpecifier getFirstBaseEntitlementWithAddOnsSpecifier(final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiers) throws SubscriptionBaseApiException {
- if (baseEntitlementWithAddOnsSpecifiers == null) {
- throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
- }
-
- final Iterator<BaseEntitlementWithAddOnsSpecifier> iterator = baseEntitlementWithAddOnsSpecifiers.iterator();
- if (!iterator.hasNext()) {
- throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
- }
-
- return iterator.next();
- }
-
@Override
public List<UUID> createBaseEntitlementsWithAddOns(final UUID accountId, final Iterable<BaseEntitlementWithAddOnsSpecifier> originalBaseEntitlementWithAddOnsSpecifiers, final boolean renameCancelledBundleIfExist, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
logCreateEntitlementsWithAOs(log, originalBaseEntitlementWithAddOnsSpecifiers);
@@ -175,6 +162,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
public List<UUID> doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(accountId, callContext);
+ final Map<UUID, Optional<EventsStream>> eventsStreamForBaseSubscriptionPerBundle = new HashMap<UUID, Optional<EventsStream>>();
final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiersAfterPlugins = updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers();
final Collection<SubscriptionBaseWithAddOnsSpecifier> subscriptionBaseWithAddOnsSpecifiers = new LinkedList<SubscriptionBaseWithAddOnsSpecifier>();
DateTime upTo = null;
@@ -185,6 +173,9 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
contextWithValidAccountRecordId);
upTo = upTo == null || upTo.compareTo(entitlementRequestedDate) < 0 ? entitlementRequestedDate : upTo;
+ // Verify if the operation is valid for that bundle
+ preCheckAddEntitlement(baseEntitlementWithAddOnsSpecifier, entitlementRequestedDate, eventsStreamForBaseSubscriptionPerBundle, callContext, contextWithValidAccountRecordId);
+
final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier = new SubscriptionBaseWithAddOnsSpecifier(baseEntitlementWithAddOnsSpecifier.getBundleId(),
baseEntitlementWithAddOnsSpecifier.getExternalKey(),
baseEntitlementWithAddOnsSpecifier.getEntitlementSpecifier(),
@@ -193,38 +184,20 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
subscriptionBaseWithAddOnsSpecifiers.add(subscriptionBaseWithAddOnsSpecifier);
}
+ // Verify if operation is allowed by looking for is_block_change on Account
+ // Note that to fully check for block_change we should also look for BlockingState at the BUNDLE/SUBSCRIPTION level in case some of the input contain a BP that already exists.
+ checkForAccountBlockingChange(accountId, upTo, contextWithValidAccountRecordId);
+
+ final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns;
try {
- // Verify if operation is allowed by looking for is_block_change on Account
- // Note that to fully check for block_change we should also look for BlockingState at the BUNDLE/SUBSCRIPTION level in case some of the input contain a BP that already exists.
- checkForAccountBlockingChange(accountId, upTo, contextWithValidAccountRecordId);
-
- final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns = subscriptionBaseInternalApi.createBaseSubscriptionsWithAddOns(subscriptionBaseWithAddOnsSpecifiers,
- renameCancelledBundleIfExist,
- contextWithValidAccountRecordId);
- final List<UUID> createdSubscriptionIds = new LinkedList<UUID>();
- final Map<BlockingState, UUID> blockingStateMap = new HashMap<BlockingState, UUID>();
- int i = 0;
- for (final Iterator<BaseEntitlementWithAddOnsSpecifier> it = baseEntitlementWithAddOnsSpecifiersAfterPlugins.iterator(); it.hasNext(); i++) {
- final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = it.next();
- for (final SubscriptionBase subscriptionBase : subscriptionsWithAddOns.get(i).getSubscriptionBaseList()) {
- final BlockingState blockingState = new DefaultBlockingState(subscriptionBase.getId(),
- BlockingStateType.SUBSCRIPTION,
- DefaultEntitlementApi.ENT_STATE_START,
- EntitlementService.ENTITLEMENT_SERVICE_NAME,
- false,
- false,
- false,
- dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), updatedPluginContext.getCreatedDate(), contextWithValidAccountRecordId));
- blockingStateMap.put(blockingState, subscriptionsWithAddOns.get(i).getBundle().getId());
-
- createdSubscriptionIds.add(subscriptionBase.getId());
- }
- }
- entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStateMap, contextWithValidAccountRecordId);
- return createdSubscriptionIds;
+ subscriptionsWithAddOns = subscriptionBaseInternalApi.createBaseSubscriptionsWithAddOns(subscriptionBaseWithAddOnsSpecifiers,
+ renameCancelledBundleIfExist,
+ contextWithValidAccountRecordId);
} catch (final SubscriptionBaseApiException e) {
throw new EntitlementApiException(e);
}
+
+ return createEntitlementEvents(baseEntitlementWithAddOnsSpecifiersAfterPlugins, subscriptionsWithAddOns, updatedPluginContext, contextWithValidAccountRecordId);
}
};
return pluginExecution.executeWithPlugin(createBaseEntitlementsWithAddOns, pluginContext);
@@ -232,94 +205,25 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
@Override
public UUID addEntitlement(final UUID bundleId, final PlanPhaseSpecifier planPhaseSpecifier, final List<PlanPhasePriceOverride> overrides, @Nullable final LocalDate entitlementEffectiveDate, @Nullable final LocalDate billingEffectiveDate,
- final boolean isMigrated, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
- logCreateEntitlement(log, bundleId, planPhaseSpecifier, overrides, entitlementEffectiveDate, billingEffectiveDate);
-
+ final boolean isMigrated, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
final EntitlementSpecifier entitlementSpecifier = new DefaultEntitlementSpecifier(planPhaseSpecifier, overrides);
- final List<EntitlementSpecifier> entitlementSpecifierList = new ArrayList<EntitlementSpecifier>();
- entitlementSpecifierList.add(entitlementSpecifier);
- final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = new DefaultBaseEntitlementWithAddOnsSpecifier(
- bundleId,
- null,
- entitlementSpecifierList,
- entitlementEffectiveDate,
- billingEffectiveDate,
- isMigrated);
- final List<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifierList = new ArrayList<BaseEntitlementWithAddOnsSpecifier>();
- baseEntitlementWithAddOnsSpecifierList.add(baseEntitlementWithAddOnsSpecifier);
-
- final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.CREATE_SUBSCRIPTION,
- null,
- null,
- baseEntitlementWithAddOnsSpecifierList,
- null,
- properties,
- callContext);
-
- final WithEntitlementPlugin<UUID> addEntitlementWithPlugin = new WithEntitlementPlugin<UUID>() {
- @Override
- public UUID doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
- final InternalCallContext context = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, callContext);
-
- final List<SubscriptionBase> subscriptionsByBundle;
- try {
- subscriptionsByBundle = subscriptionBaseInternalApi.getSubscriptionsForBundle(bundleId, null, context);
-
- if (subscriptionsByBundle == null || subscriptionsByBundle.isEmpty()) {
- throw new EntitlementApiException(ErrorCode.SUB_NO_ACTIVE_SUBSCRIPTIONS, bundleId);
- }
- } catch (final SubscriptionBaseApiException e) {
- throw new EntitlementApiException(e);
- }
- final boolean isStandalone = ProductCategory.STANDALONE.equals(subscriptionsByBundle.get(0).getCategory());
-
- final EventsStream eventsStreamForBaseSubscription = isStandalone ? null : eventsStreamBuilder.buildForBaseSubscription(bundleId, callContext);
-
- final DateTime entitlementRequestedDateRaw = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), updatedPluginContext.getCreatedDate(), context);
- DateTime entitlementRequestedDate = entitlementRequestedDateRaw;
- if (!isStandalone) {
- final DateTime baseEntitlementStartDate = eventsStreamForBaseSubscription.getEntitlementEffectiveStartDateTime();
- entitlementRequestedDate = entitlementRequestedDateRaw.isBefore(baseEntitlementStartDate) ? baseEntitlementStartDate : entitlementRequestedDateRaw;
- preCheckAddEntitlement(bundleId, entitlementRequestedDate, baseEntitlementWithAddOnsSpecifier, eventsStreamForBaseSubscription, callContext);
- }
-
- try {
- final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifierAfterPlugins = getFirstBaseEntitlementWithAddOnsSpecifier(updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers());
- final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier = new SubscriptionBaseWithAddOnsSpecifier(baseEntitlementWithAddOnsSpecifierAfterPlugins.getBundleId(),
- baseEntitlementWithAddOnsSpecifierAfterPlugins.getExternalKey(),
- baseEntitlementWithAddOnsSpecifierAfterPlugins.getEntitlementSpecifier(),
- baseEntitlementWithAddOnsSpecifierAfterPlugins.getBillingEffectiveDate(),
- baseEntitlementWithAddOnsSpecifierAfterPlugins.isMigrated());
-
- final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns = subscriptionBaseInternalApi.createBaseSubscriptionsWithAddOns(ImmutableList.<SubscriptionBaseWithAddOnsSpecifier>of(subscriptionBaseWithAddOnsSpecifier),
- false,
- context);
- final SubscriptionBaseWithAddOns subscriptionBaseWithAddOns = subscriptionsWithAddOns.get(0);
- final UUID subscriptionId = subscriptionBaseWithAddOns.getSubscriptionBaseList().get(0).getId();
-
- final BlockingState newBlockingState = new DefaultBlockingState(subscriptionId, BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
- entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(newBlockingState), subscriptionBaseWithAddOns.getBundle().getId(), context);
-
- return subscriptionId;
- } catch (final SubscriptionBaseApiException e) {
- throw new EntitlementApiException(e);
- }
- }
- };
- return pluginExecution.executeWithPlugin(addEntitlementWithPlugin, pluginContext);
- }
-
- private void preCheckAddEntitlement(final UUID bundleId, final DateTime entitlementRequestedDate, final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier, final EventsStream eventsStreamForBaseSubscription, final CallContext callContext) throws EntitlementApiException {
- if (eventsStreamForBaseSubscription.isEntitlementCancelled() ||
- (eventsStreamForBaseSubscription.isEntitlementPending() &&
- (baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate() == null ||
- baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate().compareTo(eventsStreamForBaseSubscription.getEntitlementEffectiveStartDate()) < 0))) {
- throw new EntitlementApiException(ErrorCode.SUB_GET_NO_SUCH_BASE_SUBSCRIPTION, bundleId);
- }
-
- // Check the base entitlement state is not blocked
- if (eventsStreamForBaseSubscription.isBlockChange(entitlementRequestedDate)) {
- throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getEntitlementId().toString()));
+ final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = new DefaultBaseEntitlementWithAddOnsSpecifier(bundleId,
+ null,
+ ImmutableList.<EntitlementSpecifier>of(entitlementSpecifier),
+ entitlementEffectiveDate,
+ billingEffectiveDate,
+ isMigrated);
+ final InternalCallContext context = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, callContext);
+ try {
+ final UUID accountId = subscriptionBaseInternalApi.getAccountIdFromBundleId(bundleId, context);
+ final List<UUID> createdEntitlements = createBaseEntitlementsWithAddOns(accountId,
+ ImmutableList.<BaseEntitlementWithAddOnsSpecifier>of(baseEntitlementWithAddOnsSpecifier),
+ false,
+ properties,
+ callContext);
+ return createdEntitlements.get(0);
+ } catch (final SubscriptionBaseApiException e) {
+ throw new EntitlementApiException(e);
}
}
@@ -506,8 +410,78 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
return pluginExecution.executeWithPlugin(transferWithPlugin, pluginContext);
}
- private void checkForAccountBlockingChange(final UUID accountId, @Nullable final DateTime upTo, final InternalCallContext context) throws EntitlementApiException {
+ private BaseEntitlementWithAddOnsSpecifier getFirstBaseEntitlementWithAddOnsSpecifier(final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiers) throws SubscriptionBaseApiException {
+ if (baseEntitlementWithAddOnsSpecifiers == null) {
+ throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
+ }
+
+ final Iterator<BaseEntitlementWithAddOnsSpecifier> iterator = baseEntitlementWithAddOnsSpecifiers.iterator();
+ if (!iterator.hasNext()) {
+ throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
+ }
+
+ return iterator.next();
+ }
+
+ private void preCheckAddEntitlement(final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier,
+ final DateTime entitlementRequestedDate,
+ final Map<UUID, Optional<EventsStream>> eventsStreamForBaseSubscriptionPerBundle,
+ final TenantContext callContext,
+ final InternalCallContext contextWithValidAccountRecordId) throws EntitlementApiException {
+ // TODO In the addEntitlement codepath, bundleId is always set. But technically an existing bundle could be specified by externalKey in
+ // the createBaseEntitlementsWithAddOns codepath. In that case, we should also check if that bundle is blocked (expensive though, especially
+ // since bundles are pulled again below in subscriptions)
+ if (baseEntitlementWithAddOnsSpecifier.getBundleId() != null) {
+ if (eventsStreamForBaseSubscriptionPerBundle.get(baseEntitlementWithAddOnsSpecifier.getBundleId()) == null) {
+ final List<SubscriptionBase> subscriptionsByBundle;
+ try {
+ subscriptionsByBundle = subscriptionBaseInternalApi.getSubscriptionsForBundle(baseEntitlementWithAddOnsSpecifier.getBundleId(), null, contextWithValidAccountRecordId);
+
+ if (subscriptionsByBundle == null || subscriptionsByBundle.isEmpty()) {
+ throw new EntitlementApiException(ErrorCode.SUB_NO_ACTIVE_SUBSCRIPTIONS, baseEntitlementWithAddOnsSpecifier.getBundleId());
+ }
+ } catch (final SubscriptionBaseApiException e) {
+ throw new EntitlementApiException(e);
+ }
+
+ final boolean isStandalone = Iterables.any(subscriptionsByBundle,
+ new Predicate<SubscriptionBase>() {
+ @Override
+ public boolean apply(final SubscriptionBase input) {
+ return ProductCategory.STANDALONE.equals(input.getCategory());
+ }
+ });
+
+ if (!isStandalone) {
+ final EventsStream eventsStreamForBaseSubscription = eventsStreamBuilder.buildForBaseSubscription(baseEntitlementWithAddOnsSpecifier.getBundleId(), callContext);
+ eventsStreamForBaseSubscriptionPerBundle.put(baseEntitlementWithAddOnsSpecifier.getBundleId(), Optional.<EventsStream>of(eventsStreamForBaseSubscription));
+ } else {
+ eventsStreamForBaseSubscriptionPerBundle.put(baseEntitlementWithAddOnsSpecifier.getBundleId(), Optional.<EventsStream>absent());
+ }
+ }
+
+ final Optional<EventsStream> eventsStreamForBaseSubscription = eventsStreamForBaseSubscriptionPerBundle.get(baseEntitlementWithAddOnsSpecifier.getBundleId());
+ if (eventsStreamForBaseSubscription.isPresent()) {
+ preCheckAddEntitlement(baseEntitlementWithAddOnsSpecifier.getBundleId(), entitlementRequestedDate, baseEntitlementWithAddOnsSpecifier, eventsStreamForBaseSubscription.get());
+ }
+ }
+ }
+
+ private void preCheckAddEntitlement(final UUID bundleId, final DateTime entitlementRequestedDate, final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier, final EventsStream eventsStreamForBaseSubscription) throws EntitlementApiException {
+ if (eventsStreamForBaseSubscription.isEntitlementCancelled() ||
+ (eventsStreamForBaseSubscription.isEntitlementPending() &&
+ (baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate() == null ||
+ baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate().compareTo(eventsStreamForBaseSubscription.getEntitlementEffectiveStartDate()) < 0))) {
+ throw new EntitlementApiException(ErrorCode.SUB_GET_NO_SUCH_BASE_SUBSCRIPTION, bundleId);
+ }
+ // Check the base entitlement state is not blocked
+ if (eventsStreamForBaseSubscription.isBlockChange(entitlementRequestedDate)) {
+ throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getEntitlementId().toString()));
+ }
+ }
+
+ private void checkForAccountBlockingChange(final UUID accountId, @Nullable final DateTime upTo, final InternalCallContext context) throws EntitlementApiException {
try {
final BlockingAggregator blockingAggregator = checker.getBlockedStatus(accountId, BlockingStateType.ACCOUNT, upTo, context);
if (blockingAggregator.isBlockChange()) {
@@ -517,4 +491,31 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
throw new EntitlementApiException(e);
}
}
+
+ private List<UUID> createEntitlementEvents(final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiersAfterPlugins,
+ final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns,
+ final CallContext updatedPluginContext,
+ final InternalCallContext contextWithValidAccountRecordId) throws EntitlementApiException {
+ final List<UUID> createdSubscriptionIds = new LinkedList<UUID>();
+ final Map<BlockingState, UUID> blockingStateMap = new HashMap<BlockingState, UUID>();
+ int i = 0;
+ for (final Iterator<BaseEntitlementWithAddOnsSpecifier> it = baseEntitlementWithAddOnsSpecifiersAfterPlugins.iterator(); it.hasNext(); i++) {
+ final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = it.next();
+ for (final SubscriptionBase subscriptionBase : subscriptionsWithAddOns.get(i).getSubscriptionBaseList()) {
+ final BlockingState blockingState = new DefaultBlockingState(subscriptionBase.getId(),
+ BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START,
+ EntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false,
+ false,
+ false,
+ dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), updatedPluginContext.getCreatedDate(), contextWithValidAccountRecordId));
+ blockingStateMap.put(blockingState, subscriptionsWithAddOns.get(i).getBundle().getId());
+
+ createdSubscriptionIds.add(subscriptionBase.getId());
+ }
+ }
+ entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStateMap, contextWithValidAccountRecordId);
+ return createdSubscriptionIds;
+ }
}