killbill-aplcache

Details

diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index 2fdd31e..c02fdfe 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -119,6 +119,8 @@ public interface JaxrsResource {
 
     public static final String QUERY_AUDIT = "audit";
 
+    public static final String QUERY_BCD = "bcd";
+
     public static final String QUERY_PARALLEL = "parallel";
 
     public static final String QUERY_NOTIFICATION_CALLBACK = "cb";
@@ -239,4 +241,6 @@ public interface JaxrsResource {
 
     public static final String COMBO = "combo";
 
+    public static final String BCD = "bcd";
+
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
index 5032362..114e42d 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
@@ -45,10 +45,10 @@ import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.CatalogApiException;
-import org.killbill.billing.catalog.api.CatalogUserApi;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
@@ -78,6 +78,8 @@ import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.jaxrs.util.KillbillEventHandler;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
+import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -85,6 +87,7 @@ import org.killbill.billing.util.api.TagApiException;
 import org.killbill.billing.util.api.TagDefinitionApiException;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.userrequest.CompletionUserRequestBase;
 import org.killbill.clock.Clock;
@@ -114,6 +117,10 @@ public class SubscriptionResource extends JaxRsResourceBase {
     private final EntitlementApi entitlementApi;
     private final SubscriptionApi subscriptionApi;
 
+    // Used to change subscription BCD
+    private final SubscriptionBaseInternalApi subscriptionBaseInternalApi;
+    private final InternalCallContextFactory internalCallContextFactory;
+
     @Inject
     public SubscriptionResource(final KillbillEventHandler killbillHandler,
                                 final JaxrsUriBuilder uriBuilder,
@@ -124,12 +131,16 @@ public class SubscriptionResource extends JaxRsResourceBase {
                                 final SubscriptionApi subscriptionApi,
                                 final AccountUserApi accountUserApi,
                                 final PaymentApi paymentApi,
+                                final SubscriptionBaseInternalApi subscriptionBaseInternalApi,
+                                final InternalCallContextFactory internalCallContextFactory,
                                 final Clock clock,
                                 final Context context) {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
         this.killbillHandler = killbillHandler;
         this.entitlementApi = entitlementApi;
         this.subscriptionApi = subscriptionApi;
+        this.subscriptionBaseInternalApi = subscriptionBaseInternalApi;
+        this.internalCallContextFactory = internalCallContextFactory;
     }
 
     @TimedResource
@@ -159,6 +170,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
     public Response createEntitlement(final SubscriptionJson entitlement,
                                       @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
                                       @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion,
+                                      @QueryParam(QUERY_BCD) final Integer newBCD,
                                       @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") final long timeoutSec,
                                       @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                       @HeaderParam(HDR_CREATED_BY) final String createdBy,
@@ -180,7 +192,6 @@ public class SubscriptionResource extends JaxRsResourceBase {
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
 
-
         final EntitlementCallCompletionCallback<Entitlement> callback = new EntitlementCallCompletionCallback<Entitlement>() {
             @Override
             public Entitlement doOperation(final CallContext ctx) throws InterruptedException, TimeoutException, EntitlementApiException, SubscriptionApiException, AccountApiException {
@@ -197,9 +208,19 @@ public class SubscriptionResource extends JaxRsResourceBase {
                                                                  BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList());
 
                 final List<PlanPhasePriceOverride> overrides = PhasePriceOverrideJson.toPlanPhasePriceOverrides(entitlement.getPriceOverrides(), planSpec, account.getCurrency());
-                return createAddOnEntitlement ?
+                final Entitlement result =  createAddOnEntitlement ?
                        entitlementApi.addEntitlement(getBundleIdForAddOnCreation(entitlement), spec, overrides, inputLocalDate, pluginProperties, callContext) :
                        entitlementApi.createBaseEntitlement(account.getId(), spec, entitlement.getExternalKey(), overrides, inputLocalDate, pluginProperties, callContext);
+
+                if (newBCD != null) {
+                    final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(result.getId(), ObjectType.SUBSCRIPTION, callContext);
+                    try {
+                        subscriptionBaseInternalApi.updateBCD(result.getId(), newBCD, internalCallContext);
+                    } catch (final SubscriptionBaseApiException e) {
+                        throw new SubscriptionApiException(e);
+                    }
+                }
+                return result;
             }
 
             private UUID getBundleIdForAddOnCreation(final SubscriptionJson entitlement) throws SubscriptionApiException {
@@ -235,15 +256,15 @@ public class SubscriptionResource extends JaxRsResourceBase {
     @ApiOperation(value = "Create an entitlement with addOn products")
     @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid entitlement supplied")})
     public Response createEntitlementWithAddOns(final List<SubscriptionJson> entitlements,
-                                      @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
-                                      @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion,
-                                      @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") final long timeoutSec,
-                                      @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
-                                      @HeaderParam(HDR_CREATED_BY) final String createdBy,
-                                      @HeaderParam(HDR_REASON) final String reason,
-                                      @HeaderParam(HDR_COMMENT) final String comment,
-                                      @javax.ws.rs.core.Context final HttpServletRequest request,
-                                      @javax.ws.rs.core.Context final UriInfo uriInfo) throws EntitlementApiException, AccountApiException, SubscriptionApiException {
+                                                @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
+                                                @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion,
+                                                @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") final long timeoutSec,
+                                                @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
+                                                @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                                @HeaderParam(HDR_REASON) final String reason,
+                                                @HeaderParam(HDR_COMMENT) final String comment,
+                                                @javax.ws.rs.core.Context final HttpServletRequest request,
+                                                @javax.ws.rs.core.Context final UriInfo uriInfo) throws EntitlementApiException, AccountApiException, SubscriptionApiException {
 
         Preconditions.checkArgument(Iterables.size(entitlements) > 0, "Subscription list mustn't be null or empty.");
 
@@ -273,11 +294,11 @@ public class SubscriptionResource extends JaxRsResourceBase {
 
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final SubscriptionJson baseEntitlement = Iterables.tryFind(entitlements, new Predicate<SubscriptionJson>() {
-                    @Override
-                    public boolean apply(final SubscriptionJson subscription) {
-                        return ProductCategory.BASE.toString().equalsIgnoreCase(subscription.getProductCategory());
-                    }
-            }).orNull();
+            @Override
+            public boolean apply(final SubscriptionJson subscription) {
+                return ProductCategory.BASE.toString().equalsIgnoreCase(subscription.getProductCategory());
+            }
+        }).orNull();
 
         verifyNonNull(baseEntitlement.getAccountId(), "SubscriptionJson accountId needs to be set for BASE product.");
 
@@ -298,8 +319,8 @@ public class SubscriptionResource extends JaxRsResourceBase {
                                                                                          BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList(), null);
 
                     final PlanSpecifier planSpec = new PlanSpecifier(entitlement.getProductName(),
-                                                                    ProductCategory.valueOf(entitlement.getProductCategory()),
-                                                                    BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList());
+                                                                     ProductCategory.valueOf(entitlement.getProductCategory()),
+                                                                     BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList());
                     final List<PlanPhasePriceOverride> overrides = PhasePriceOverrideJson.toPlanPhasePriceOverrides(entitlement.getPriceOverrides(), planSpec, account.getCurrency());
 
                     EntitlementSpecifier specifier = new EntitlementSpecifier() {
@@ -510,6 +531,30 @@ public class SubscriptionResource extends JaxRsResourceBase {
         return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, callContext);
     }
 
+    @TimedResource
+    @PUT
+    @Path("/{subscriptionId:" + UUID_PATTERN + "}/" + BCD +"/{newBCD:" + NUMBER_PATTERN + "}")
+    @ApiOperation(value = "Update the BCD associated to a subscription")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid entitlement supplied")})
+    public Response updateSubscriptionBCD(@PathParam(ID_PARAM_NAME) final String id,
+                                          @PathParam("newBCD") final Integer newBCD,
+                                          @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                          @HeaderParam(HDR_REASON) final String reason,
+                                          @HeaderParam(HDR_COMMENT) final String comment,
+                                          @javax.ws.rs.core.Context final UriInfo uriInfo,
+                                          @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException {
+
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        final UUID subscriptionId = UUID.fromString(id);
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(subscriptionId, ObjectType.SUBSCRIPTION, callContext);
+        try {
+            subscriptionBaseInternalApi.updateBCD(subscriptionId, newBCD, internalCallContext);
+        } catch (final SubscriptionBaseApiException e) {
+            throw new SubscriptionApiException(e);
+        }
+        return Response.status(Status.OK).build();
+    }
+
     private static final class CompletionUserRequestEntitlement extends CompletionUserRequestBase {
 
         public CompletionUserRequestEntitlement(final UUID userToken) {
@@ -570,7 +615,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
         public Response withSynchronization(final EntitlementCallCompletionCallback<T> callback,
                                             final long timeoutSec,
                                             final boolean callCompletion,
-                                            final CallContext callContext) throws SubscriptionApiException, AccountApiException, EntitlementApiException{
+                                            final CallContext callContext) throws SubscriptionApiException, AccountApiException, EntitlementApiException {
             final CompletionUserRequestEntitlement waiter = callCompletion ? new CompletionUserRequestEntitlement(callContext.getUserToken()) : null;
             try {
                 if (waiter != null) {
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
index 0c826dd..1a58c33 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
@@ -31,7 +31,8 @@ import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
-import org.killbill.billing.client.KillBillClientException;
+import org.killbill.billing.client.JaxrsResource;
+import org.killbill.billing.client.KillBillHttpClient;
 import org.killbill.billing.client.model.Account;
 import org.killbill.billing.client.model.Bundle;
 import org.killbill.billing.client.model.Invoice;
@@ -42,6 +43,11 @@ import org.killbill.billing.util.api.AuditLevel;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.ning.http.client.Response;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
@@ -297,4 +303,28 @@ public class TestEntitlement extends TestJaxrsBase {
         final Subscription objFromJson = killBillClient.getSubscription(entitlementJson.getSubscriptionId());
         Assert.assertTrue(objFromJson.equals(entitlementJson));
     }
+
+    @Test(groups = "slow", description = "Verify we can move the BCD associated with the subscription")
+    public void testMoveEntitlementBCD() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account accountJson = createAccountWithDefaultPaymentMethod();
+
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+
+        final Subscription entitlementJson = createEntitlement(accountJson.getAccountId(), "99999", productName,
+                                                               ProductCategory.BASE, term, true);
+
+        // Until we have a proper java client API (depends on our branching strategy)
+        final Multimap options = HashMultimap.create();
+        options.put(KillBillHttpClient.AUDIT_OPTION_CREATED_BY, createdBy);
+        options.put(KillBillHttpClient.AUDIT_OPTION_REASON, reason);
+        options.put(KillBillHttpClient.AUDIT_OPTION_COMMENT, comment);
+
+        final String uri = JaxrsResource.SUBSCRIPTIONS_PATH + "/" + entitlementJson.getSubscriptionId() + "/bcd/" + 9;
+        final Response response = killBillHttpClient.doPut(uri, null, options);
+        Assert.assertEquals(response.getStatusCode(), 200);
+    }
 }