killbill-aplcache
Changes
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java 151(+143 -8)
Details
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
index 5aeffe1..b3ac142 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -29,13 +29,22 @@ import java.util.UUID;
import javax.inject.Inject;
import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.entitlement.AccountEntitlements;
import org.killbill.billing.entitlement.EntitlementInternalApi;
+import org.killbill.billing.entitlement.EntitlementService;
+import org.killbill.billing.entitlement.api.EntitlementPluginExecution.WithEntitlementPlugin;
import org.killbill.billing.entitlement.engine.core.EntitlementUtils;
+import org.killbill.billing.entitlement.plugin.api.EntitlementContext;
+import org.killbill.billing.entitlement.plugin.api.OperationType;
+import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
@@ -46,6 +55,7 @@ import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.customfield.ShouldntHappenException;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.SourcePaginationBuilder;
+import org.killbill.clock.Clock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -80,17 +90,29 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
}
};
+ private final AccountInternalApi accountApi;
private final EntitlementInternalApi entitlementInternalApi;
private final SubscriptionBaseInternalApi subscriptionBaseInternalApi;
private final InternalCallContextFactory internalCallContextFactory;
private final EntitlementUtils entitlementUtils;
+ private final Clock clock;
+ private final EntitlementPluginExecution pluginExecution;
+
@Inject
- public DefaultSubscriptionApi(final EntitlementInternalApi entitlementInternalApi, final SubscriptionBaseInternalApi subscriptionInternalApi,
- final InternalCallContextFactory internalCallContextFactory, final EntitlementUtils entitlementUtils) {
+ public DefaultSubscriptionApi(final AccountInternalApi accountApi,
+ final EntitlementInternalApi entitlementInternalApi,
+ final SubscriptionBaseInternalApi subscriptionInternalApi,
+ final InternalCallContextFactory internalCallContextFactory,
+ final Clock clock,
+ final EntitlementPluginExecution pluginExecution,
+ final EntitlementUtils entitlementUtils) {
+ this.accountApi = accountApi;
this.entitlementInternalApi = entitlementInternalApi;
this.subscriptionBaseInternalApi = subscriptionInternalApi;
this.internalCallContextFactory = internalCallContextFactory;
+ this.clock = clock;
+ this.pluginExecution = pluginExecution;
this.entitlementUtils = entitlementUtils;
}
@@ -142,12 +164,6 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
}
@Override
- public void updateExternalKey(final UUID uuid, final String newExternalKey, final CallContext callContext) {
- final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(callContext);
- subscriptionBaseInternalApi.updateExternalKey(uuid, newExternalKey, internalContext);
- }
-
- @Override
public List<SubscriptionBundle> getSubscriptionBundlesForAccountIdAndExternalKey(final UUID accountId, final String externalKey, final TenantContext context) throws SubscriptionApiException {
return ImmutableList.<SubscriptionBundle>copyOf(Iterables.<SubscriptionBundle>filter(getSubscriptionBundlesForAccount(accountId, context),
new Predicate<SubscriptionBundle>() {
@@ -240,6 +256,125 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
);
}
+ @Override
+ public void updateExternalKey(final UUID bundleId, final String newExternalKey, final CallContext callContext) throws EntitlementApiException {
+ final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(callContext);
+
+ final SubscriptionBaseBundle bundle;
+ final ImmutableAccountData account;
+ try {
+ bundle = subscriptionBaseInternalApi.getBundleFromId(bundleId, internalCallContext);
+ account = accountApi.getImmutableAccountDataById(bundle.getAccountId(), internalCallContext);
+ } catch (final SubscriptionBaseApiException e) {
+ throw new EntitlementApiException(e);
+ } catch (AccountApiException e) {
+ throw new EntitlementApiException(e);
+ }
+
+ final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.UPDATE_BUNDLE_EXTERNAL_KEY,
+ bundle.getAccountId(),
+ null,
+ bundleId,
+ newExternalKey,
+ new ArrayList<EntitlementSpecifier>(),
+ new LocalDate(clock.getUTCNow(), account.getTimeZone()),
+ ImmutableList.<PluginProperty>of(),
+ callContext);
+
+ final WithEntitlementPlugin<Void> updateExternalKeyWithPlugin = new WithEntitlementPlugin<Void>() {
+
+ final InternalCallContext internalCallContextWithValidAccountId = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+
+ @Override
+ public Void doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
+ subscriptionBaseInternalApi.updateExternalKey(bundleId, newExternalKey, internalCallContextWithValidAccountId);
+ return null;
+ }
+ };
+ pluginExecution.executeWithPlugin(updateExternalKeyWithPlugin, pluginContext);
+ }
+
+ @Override
+ public void addBlockingState(final BlockingState blockingState, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+
+ // This is in no way an exhaustive arg validation, but to to ensure plugin would not hijack private entitlement state or service name
+ if (blockingState.getService() == null || blockingState.getService().equals(EntitlementService.ENTITLEMENT_SERVICE_NAME)) {
+ throw new EntitlementApiException(ErrorCode.SUB_BLOCKING_STATE_INVALID_ARG, "Need to specify a valid serviceName");
+ }
+
+ if (blockingState.getStateName() == null ||
+ blockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED) ||
+ blockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_BLOCKED) ||
+ blockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CLEAR)) {
+ throw new EntitlementApiException(ErrorCode.SUB_BLOCKING_STATE_INVALID_ARG, "Need to specify a valid stateName");
+ }
+
+ final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(callContext);
+
+ final ImmutableAccountData account;
+ final UUID accountId;
+ final UUID bundleId;
+ final String externalKey;
+ try {
+ switch (blockingState.getType()) {
+ case ACCOUNT:
+ account = accountApi.getImmutableAccountDataById(blockingState.getBlockedId(), internalCallContext);
+ externalKey = account.getExternalKey();
+ accountId = account.getId();
+ bundleId = null;
+ break;
+
+ case SUBSCRIPTION_BUNDLE:
+ final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getBundleFromId(blockingState.getBlockedId(), internalCallContext);
+ externalKey = bundle.getExternalKey();
+ bundleId = bundle.getId();
+ accountId = bundle.getAccountId();
+ account = accountApi.getImmutableAccountDataById(accountId, internalCallContext);
+ break;
+
+ case SUBSCRIPTION:
+ final Entitlement entitlement = entitlementInternalApi.getEntitlementForId(blockingState.getBlockedId(), internalCallContext);
+ bundleId = entitlement.getBundleId();
+ accountId = entitlement.getAccountId();
+ account = accountApi.getImmutableAccountDataById(accountId, internalCallContext);
+ externalKey = null;
+ break;
+
+ default:
+ throw new IllegalStateException("Invalid blockingStateType " + blockingState.getType());
+ }
+ } catch (final AccountApiException e) {
+ throw new EntitlementApiException(e);
+ } catch (final SubscriptionBaseApiException e) {
+ throw new EntitlementApiException(e);
+ }
+
+
+
+ final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.ADD_BLOCKING_STATE,
+ accountId,
+ null,
+ bundleId,
+ externalKey,
+ new ArrayList<EntitlementSpecifier>(),
+ new LocalDate(blockingState.getEffectiveDate(), account.getTimeZone()),
+ properties,
+ callContext);
+
+
+ final WithEntitlementPlugin<Void> addBlockingStateWithPlugin = new WithEntitlementPlugin<Void>() {
+
+ final InternalCallContext internalCallContextWithValidAccountId = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+
+ @Override
+ public Void doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
+ entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingState, internalCallContextWithValidAccountId);
+ return null;
+ }
+ };
+ pluginExecution.executeWithPlugin(addBlockingStateWithPlugin, pluginContext);
+ }
+
private List<SubscriptionBundle> getSubscriptionBundlesForAccount(final UUID accountId, final TenantContext tenantContext) throws SubscriptionApiException {
// Retrieve entitlements
final AccountEntitlements accountEntitlements;
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
index 59f2c89..7bda096 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
@@ -153,7 +153,7 @@ public class DefaultEntitlementApiBase {
public void pause(final UUID bundleId, final LocalDate localEffectiveDate, final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext) throws EntitlementApiException {
- final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.PAUSE_SUBSCRIPTION,
+ final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.PAUSE_BUNDLE,
null,
null,
bundleId,
@@ -206,7 +206,7 @@ public class DefaultEntitlementApiBase {
public void resume(final UUID bundleId, final LocalDate localEffectiveDate, final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext) throws EntitlementApiException {
- final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.RESUME_SUBSCRIPTION,
+ final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.RESUME_BUNDLE,
null,
null,
bundleId,
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
index 55dcdad..3b74850 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
@@ -22,6 +22,7 @@ import java.util.UUID;
import org.joda.time.LocalDate;
import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
import org.killbill.billing.payment.api.PluginProperty;
import org.testng.Assert;
import org.testng.annotations.Test;
@@ -318,6 +319,44 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
}
+
+ @Test(groups = "slow")
+ public void testAddBlockingState() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+
+ final LocalDate initialDate = new LocalDate(2013, 8, 7);
+ clock.setDay(initialDate);
+
+ final Account account = accountApi.createAccount(getAccountData(7), callContext);
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+ testListener.pushExpectedEvent(NextEvent.CREATE);
+ final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+
+ testListener.pushExpectedEvent(NextEvent.BLOCK);
+ final BlockingState state1 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, "accountBlock", "svc1", false, true, false, clock.getUTCNow());
+ subscriptionApi.addBlockingState(state1, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ Entitlement updateEntitlement = entitlementApi.getEntitlementForId(createdEntitlement.getId(), callContext);
+ Assert.assertEquals(updateEntitlement.getState(), EntitlementState.BLOCKED);
+
+ clock.addDays(1);
+
+ testListener.pushExpectedEvent(NextEvent.BLOCK);
+ final BlockingState state2 = new DefaultBlockingState(createdEntitlement.getId(), BlockingStateType.SUBSCRIPTION, "subscriptionBlock", "svc2", false, false, false, clock.getUTCNow());
+ subscriptionApi.addBlockingState(state2, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ // Still blocked because this is a different service
+ updateEntitlement = entitlementApi.getEntitlementForId(createdEntitlement.getId(), callContext);
+ Assert.assertEquals(updateEntitlement.getState(), EntitlementState.BLOCKED);
+
+ }
+
+
private void checkSubscriptionEventAuditLog(final List<SubscriptionEvent> transitions, final int idx, final SubscriptionEventType expectedType) {
assertEquals(transitions.get(idx).getSubscriptionEventType(), expectedType);
final List<AuditLog> auditLogs = auditUserApi.getAuditLogs(transitions.get(idx).getId(), transitions.get(idx).getSubscriptionEventType().getObjectType(), AuditLevel.FULL, callContext);