Skip to content

Commit

Permalink
Combo call to create subscriptions with add-ons. #199
Browse files Browse the repository at this point in the history
  • Loading branch information
Matias Aguero committed Oct 27, 2015
1 parent 324ad47 commit fa51ca9
Show file tree
Hide file tree
Showing 14 changed files with 607 additions and 2 deletions.
Expand Up @@ -28,6 +28,7 @@
import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.entitlement.api.EntitlementAOStatusDryRun;
import org.killbill.billing.entitlement.api.EntitlementSpecifier;
import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
import org.killbill.billing.invoice.api.DryRunArguments;
import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
Expand All @@ -40,6 +41,9 @@ public interface SubscriptionBaseInternalApi {
public SubscriptionBase createSubscription(UUID bundleId, PlanPhaseSpecifier spec, List<PlanPhasePriceOverride> overrides, DateTime requestedDateWithMs,
InternalCallContext context) throws SubscriptionBaseApiException;

public SubscriptionBase createBaseSubscriptionWithAddOns(UUID bundleId, Iterable<EntitlementSpecifier> entitlements, DateTime requestedDateWithMs,
InternalCallContext context) throws SubscriptionBaseApiException;

public SubscriptionBaseBundle createBundleForAccount(UUID accountId, String bundleName, InternalCallContext context)
throws SubscriptionBaseApiException;

Expand Down
Expand Up @@ -31,6 +31,7 @@
import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.AccountEventsStreams;
import org.killbill.billing.entitlement.EntitlementService;
import org.killbill.billing.entitlement.EventsStream;
Expand Down Expand Up @@ -155,6 +156,58 @@ public Entitlement doCall(final EntitlementApi entitlementApi, final Entitlement
return pluginExecution.executeWithPlugin(createBaseEntitlementWithPlugin, pluginContext);
}

@Override
public Entitlement createBaseEntitlementWithAddOns(final UUID accountId, final Iterable<EntitlementSpecifier> entitlementSpecifier, final LocalDate effectiveDate,
final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {

final EntitlementSpecifier baseSpecifier = Iterables.tryFind(entitlementSpecifier, new Predicate<EntitlementSpecifier>() {
@Override
public boolean apply(final EntitlementSpecifier specifier) {
return specifier.getPlanPhaseSpecifier() != null && ProductCategory.BASE.equals(specifier.getPlanPhaseSpecifier().getProductCategory());
}
}).orNull();

if (baseSpecifier == null) {
throw new EntitlementApiException(new IllegalArgumentException(), ErrorCode.SUB_CREATE_NO_BP.getCode(), "Missing Base Subscription.");
}

final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.CREATE_SUBSCRIPTION,
accountId,
null,
null,
baseSpecifier.getPlanPhaseSpecifier(),
baseSpecifier.getExternalkey(),
baseSpecifier.getOverrides(),
effectiveDate,
properties,
callContext);

final WithEntitlementPlugin<Entitlement> createBaseEntitlementWithAddOn = new WithEntitlementPlugin<Entitlement>() {
@Override
public Entitlement doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(accountId, callContext);

try {
final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.createBundleForAccount(accountId, baseSpecifier.getExternalkey(), contextWithValidAccountRecordId);

final DateTime referenceTime = clock.getUTCNow();
final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), referenceTime, contextWithValidAccountRecordId);
final SubscriptionBase subscription = subscriptionBaseInternalApi.createBaseSubscriptionWithAddOns(bundle.getId(), entitlementSpecifier, requestedDate, contextWithValidAccountRecordId);

return new DefaultEntitlement(subscription.getId(), eventsStreamBuilder, entitlementApi, pluginExecution,
blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory, callContext);


} catch (SubscriptionBaseApiException e) {
throw new EntitlementApiException(e);
}

}
};
return pluginExecution.executeWithPlugin(createBaseEntitlementWithAddOn, pluginContext);
}

@Override
public Entitlement addEntitlement(final UUID bundleId, final PlanPhaseSpecifier planPhaseSpecifier, final List<PlanPhasePriceOverride> overrides, final LocalDate effectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {

Expand Down
@@ -0,0 +1,53 @@
/*
* Copyright 2014-2015 Groupon, Inc
* Copyright 2014-2015 The Billing Project, LLC
*
* The Billing Project 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 org.killbill.billing.entitlement.api;

import java.util.List;

import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.catalog.api.PlanSpecifier;

public class DefaultEntitlementSpecifier implements EntitlementSpecifier {

private String externalkey;
private PlanPhaseSpecifier PlanPhaseSpecifier;
private List<PlanPhasePriceOverride> overrides;

public DefaultEntitlementSpecifier(final String externalkey, final org.killbill.billing.catalog.api.PlanPhaseSpecifier planPhaseSpecifier, final List<PlanPhasePriceOverride> overrides) {
this.externalkey = externalkey;
PlanPhaseSpecifier = planPhaseSpecifier;
this.overrides = overrides;
}

@Override
public String getExternalkey() {
return externalkey;
}

@Override
public PlanPhaseSpecifier getPlanPhaseSpecifier() {
return PlanPhaseSpecifier;
}

@Override
public List<PlanPhasePriceOverride> getOverrides() {
return overrides;
}

}
Expand Up @@ -16,6 +16,7 @@

package org.killbill.billing.entitlement.api;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

Expand All @@ -42,7 +43,10 @@
import com.google.common.collect.ImmutableList;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;

public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedDB {

Expand Down Expand Up @@ -485,4 +489,106 @@ public void testCreateEntitlementInThePast() throws AccountApiException, Entitle

}

@Test(groups = "slow")
public void testCreateBaseEntitlementWithAddOns() throws AccountApiException, EntitlementApiException, SubscriptionBaseApiException {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);

final Account account = accountApi.createAccount(getAccountData(7), callContext);

final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Cleaning", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);

EntitlementSpecifier baseEntitlementSpecifier = new DefaultEntitlementSpecifier("baseExternalKey", baseSpec, null);
EntitlementSpecifier addOnEntitlementSpecifier = new DefaultEntitlementSpecifier("addOnExternalKey", addOnSpec, null);

final List<EntitlementSpecifier> specifierList = new ArrayList<EntitlementSpecifier>();
specifierList.add(baseEntitlementSpecifier);
specifierList.add(addOnEntitlementSpecifier);

// Keep the same object for the whole test, to make sure we refresh its state before r/w calls
testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE);
final Entitlement entitlement = entitlementApi.createBaseEntitlementWithAddOns(account.getId(), specifierList, initialDate, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();

assertNotNull(entitlement);

final List<Entitlement> allEntitlementsForBundle = entitlementApi.getAllEntitlementsForBundle(entitlement.getBundleId(), callContext);
assertTrue(allEntitlementsForBundle.size() == 2);

final Entitlement baseEntitlement = allEntitlementsForBundle.get(0);
final Entitlement addOnEntitlement = allEntitlementsForBundle.get(1);

assertEquals(baseEntitlement.getLastActiveProduct().getName(), "Pistol");
assertEquals(baseEntitlement.getLastActiveProductCategory(), ProductCategory.BASE);

assertEquals(addOnEntitlement.getLastActiveProduct().getName(), "Cleaning");
assertEquals(addOnEntitlement.getLastActiveProductCategory(), ProductCategory.ADD_ON);

}

@Test(groups = "slow")
public void testCreateBaseEntitlementWithInvalidAddOn() throws AccountApiException, EntitlementApiException {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);

final Account account = accountApi.createAccount(getAccountData(7), callContext);

final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Invalid", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);

EntitlementSpecifier baseEntitlementSpecifier = new DefaultEntitlementSpecifier("baseExternalKey", baseSpec, null);
EntitlementSpecifier addOnEntitlementSpecifier = new DefaultEntitlementSpecifier("addOnExternalKey", addOnSpec, null);

final List<EntitlementSpecifier> specifierList = new ArrayList<EntitlementSpecifier>();
specifierList.add(baseEntitlementSpecifier);
specifierList.add(addOnEntitlementSpecifier);

// Keep the same object for the whole test, to make sure we refresh its state before r/w calls
testListener.pushExpectedEvents();
try {
entitlementApi.createBaseEntitlementWithAddOns(account.getId(), specifierList, initialDate, ImmutableList.<PluginProperty>of(), callContext);
fail();
} catch (EntitlementApiException e) {
assertEquals(e.getMessage(), "Could not find any product named 'Invalid'");
}
assertListenerStatus();

final List<Entitlement> allEntitlementsForAccount = entitlementApi.getAllEntitlementsForAccountId(account.getId(), callContext);
assertTrue(allEntitlementsForAccount.size() == 0);

}

@Test(groups = "slow")
public void testCreateBaseEntitlementWithoutBaseEntitlement() throws AccountApiException, EntitlementApiException {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);

final Account account = accountApi.createAccount(getAccountData(7), callContext);

final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Cleaning", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Bullets", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);

EntitlementSpecifier addOnEntitlementSpecifier1 = new DefaultEntitlementSpecifier("addOnExternalKey", baseSpec, null);
EntitlementSpecifier addOnEntitlementSpecifier2 = new DefaultEntitlementSpecifier("addOnExternalKey2", addOnSpec, null);

final List<EntitlementSpecifier> specifierList = new ArrayList<EntitlementSpecifier>();
specifierList.add(addOnEntitlementSpecifier1);
specifierList.add(addOnEntitlementSpecifier2);

// Keep the same object for the whole test, to make sure we refresh its state before r/w calls
testListener.pushExpectedEvents();
try {
entitlementApi.createBaseEntitlementWithAddOns(account.getId(), specifierList, initialDate, ImmutableList.<PluginProperty>of(), callContext);
fail();
} catch (EntitlementApiException e) {
assertEquals(e.getMessage(), "Missing Base Subscription.");
}
assertListenerStatus();

final List<Entitlement> allEntitlementsForAccount = entitlementApi.getAllEntitlementsForAccountId(account.getId(), callContext);
assertTrue(allEntitlementsForAccount.size() == 0);

}

}
Expand Up @@ -470,4 +470,9 @@ protected void verifyNonNull(final Object... elements) {
Preconditions.checkArgument(expression, errorMessage);
}
}

protected void verifyNumberOfElements(int actual, int expected, String errorMessage) {
Preconditions.checkArgument(actual == expected, errorMessage);
}

}

0 comments on commit fa51ca9

Please sign in to comment.