Skip to content

Commit

Permalink
Introduce new internal account api to optimize code path and avoid fe…
Browse files Browse the repository at this point in the history
…thcing account data from DB when the data is immutable.

This change does not yet introduce the caching layer that is reuiqred to fully take advantage of that change.
  • Loading branch information
sbrossie committed Oct 1, 2015
1 parent 3b6b157 commit 5bec17d
Show file tree
Hide file tree
Showing 54 changed files with 358 additions and 297 deletions.
Expand Up @@ -295,15 +295,18 @@ public Account mergeWithDelegate(final Account currentAccount) {
accountData.setCurrency(currentAccount.getCurrency());
}

if (billCycleDayLocal != null && billCycleDayLocal != DEFAULT_BILLING_CYCLE_DAY_LOCAL && currentAccount.getBillCycleDayLocal() != null && currentAccount.getBillCycleDayLocal() != DEFAULT_BILLING_CYCLE_DAY_LOCAL && !billCycleDayLocal.equals(currentAccount.getBillCycleDayLocal())) {
throw new IllegalArgumentException(String.format("Killbill doesn't support updating the account BCD yet: new=%s, current=%s",
billCycleDayLocal, currentAccount.getBillCycleDayLocal()));
} else if (billCycleDayLocal != null && billCycleDayLocal != DEFAULT_BILLING_CYCLE_DAY_LOCAL) {
// Junction sets it

if (currentAccount.getBillCycleDayLocal() != DEFAULT_BILLING_CYCLE_DAY_LOCAL && // There is already a BCD set
billCycleDayLocal != null && // and the proposed date is not null
billCycleDayLocal != DEFAULT_BILLING_CYCLE_DAY_LOCAL && // and the proposed date is not 0
!currentAccount.getBillCycleDayLocal().equals(billCycleDayLocal)) { // and it does not match we we have
throw new IllegalArgumentException(String.format("Killbill doesn't support updating the account BCD yet: new=%s, current=%s", billCycleDayLocal, currentAccount.getBillCycleDayLocal()));
} else if (currentAccount.getBillCycleDayLocal() == DEFAULT_BILLING_CYCLE_DAY_LOCAL && // There is *not* already a BCD set
billCycleDayLocal != null && // and the value proposed is not null
billCycleDayLocal != DEFAULT_BILLING_CYCLE_DAY_LOCAL) { // and the proposed date is not 0
accountData.setBillCycleDayLocal(billCycleDayLocal);
} else {
// Default to current value
accountData.setBillCycleDayLocal(currentAccount.getBillCycleDayLocal() == null ? DEFAULT_BILLING_CYCLE_DAY_LOCAL : currentAccount.getBillCycleDayLocal());
accountData.setBillCycleDayLocal(currentAccount.getBillCycleDayLocal());
}

// Set all updatable fields with the new values if non null, otherwise defaults to the current values
Expand Down Expand Up @@ -336,6 +339,10 @@ public Account mergeWithDelegate(final Account currentAccount) {
return new DefaultAccount(currentAccount.getId(), accountData);
}

public ImmutableAccountData toImmutableAccountData() {
return new DefaultImmutableAccountData(this);
}

@Override
public String toString() {
return "DefaultAccount [externalKey=" + externalKey +
Expand Down
@@ -0,0 +1,62 @@
/*
* 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.account.api;

import java.util.UUID;

import org.joda.time.DateTimeZone;
import org.killbill.billing.catalog.api.Currency;

public class DefaultImmutableAccountData implements ImmutableAccountData {

private final UUID id;
private final String externalKey;
private final Currency currency;
private final DateTimeZone dateTimeZone;

public DefaultImmutableAccountData(final UUID id, final String externalKey, final Currency currency, final DateTimeZone dateTimeZone) {
this.id = id;
this.externalKey = externalKey;
this.currency = currency;
this.dateTimeZone = dateTimeZone;
}

public DefaultImmutableAccountData(final Account account) {
this(account.getId(), account.getExternalKey(), account.getCurrency(), account.getTimeZone());
}

@Override
public UUID getId() {
return id;
}

@Override
public String getExternalKey() {
return externalKey;
}

@Override
public Currency getCurrency() {
return currency;
}

@Override
public DateTimeZone getTimeZone() {
return dateTimeZone;
}
}
Expand Up @@ -24,23 +24,22 @@
import org.killbill.billing.ErrorCode;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.account.api.AccountData;
import org.killbill.billing.account.api.AccountEmail;
import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.account.api.DefaultAccount;
import org.killbill.billing.account.api.DefaultAccountEmail;
import org.killbill.billing.account.api.DefaultImmutableAccountData;
import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.account.api.MutableAccountData;
import org.killbill.billing.account.dao.AccountDao;
import org.killbill.billing.account.dao.AccountEmailModelDao;
import org.killbill.billing.account.dao.AccountModelDao;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.util.entity.DefaultPagination;
import org.killbill.billing.util.entity.Pagination;

import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;

public class DefaultAccountInternalApi implements AccountInternalApi {

Expand All @@ -67,20 +66,25 @@ public Account getAccountByRecordId(final Long recordId, final InternalTenantCon
}

@Override
public void updateAccount(final String externalKey, final AccountData accountData,
public void updateBCD(final String externalKey, final int bcd,
final InternalCallContext context) throws AccountApiException {
final Account currentAccount = getAccountByKey(externalKey, context);
if (currentAccount == null) {
throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_KEY, externalKey);
}

// Set unspecified (null) fields to their current values
final Account updatedAccount = new DefaultAccount(currentAccount.getId(), accountData);
final AccountModelDao accountToUpdate = new AccountModelDao(currentAccount.getId(), updatedAccount.mergeWithDelegate(currentAccount));

final MutableAccountData mutableAccountData = currentAccount.toMutableAccountData();
mutableAccountData.setBillCycleDayLocal(bcd);
final AccountModelDao accountToUpdate = new AccountModelDao(currentAccount.getId(), mutableAccountData);
accountDao.update(accountToUpdate, context);
}

@Override
public int getBCD(final UUID accountId, final InternalTenantContext context) throws AccountApiException {
final Account account = getAccountById(accountId, context);
return account.getBillCycleDayLocal();
}

@Override
public List<AccountEmail> getEmails(final UUID accountId,
final InternalTenantContext context) {
Expand Down Expand Up @@ -119,11 +123,31 @@ public UUID getByRecordId(final Long recordId, final InternalTenantContext conte
return accountModelDao.getId();
}

@Override
public ImmutableAccountData getImmutableAccountDataById(final UUID accountId, final InternalTenantContext context) throws AccountApiException {
final Account account = getAccountById(accountId, context);
return new DefaultImmutableAccountData(account);
}

@Override
public ImmutableAccountData getImmutableAccountDataByKey(final String key, final InternalTenantContext context) throws AccountApiException {
final Account account = getAccountByKey(key, context);
return new DefaultImmutableAccountData(account);
}

@Override
public ImmutableAccountData getImmutableAccountDataByRecordId(final Long recordId, final InternalTenantContext context) throws AccountApiException {
final Account account = getAccountByRecordId(recordId, context);
return new DefaultImmutableAccountData(account);
}

private AccountModelDao getAccountModelDaoByRecordId(final Long recordId, final InternalTenantContext context) throws AccountApiException {
final AccountModelDao accountModelDao = accountDao.getByRecordId(recordId, context);
if (accountModelDao == null) {
throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_RECORD_ID, recordId);
}
return accountModelDao;
}


}
Expand Up @@ -21,23 +21,31 @@

import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.util.entity.Pagination;

public interface AccountInternalApi {

public Account getAccountByKey(String key, InternalTenantContext context) throws AccountApiException;
Account getAccountByKey(String key, InternalTenantContext context) throws AccountApiException;

public Account getAccountById(UUID accountId, InternalTenantContext context) throws AccountApiException;
Account getAccountById(UUID accountId, InternalTenantContext context) throws AccountApiException;

public Account getAccountByRecordId(Long recordId, InternalTenantContext context) throws AccountApiException;
Account getAccountByRecordId(Long recordId, InternalTenantContext context) throws AccountApiException;

public void updateAccount(String key, AccountData accountData, InternalCallContext context) throws AccountApiException;
void updateBCD(String key, int bcd, InternalCallContext context) throws AccountApiException;

public List<AccountEmail> getEmails(UUID accountId, InternalTenantContext context);
int getBCD(UUID accountId, InternalTenantContext context) throws AccountApiException;

public void removePaymentMethod(UUID accountId, InternalCallContext context) throws AccountApiException;
List<AccountEmail> getEmails(UUID accountId, InternalTenantContext context);

public void updatePaymentMethod(UUID accountId, UUID paymentMethodId, InternalCallContext context) throws AccountApiException;
void removePaymentMethod(UUID accountId, InternalCallContext context) throws AccountApiException;

void updatePaymentMethod(UUID accountId, UUID paymentMethodId, InternalCallContext context) throws AccountApiException;

UUID getByRecordId(Long recordId, InternalTenantContext context) throws AccountApiException;

ImmutableAccountData getImmutableAccountDataById(UUID accountId, InternalTenantContext context) throws AccountApiException;

ImmutableAccountData getImmutableAccountDataByKey(String key, InternalTenantContext context) throws AccountApiException;

ImmutableAccountData getImmutableAccountDataByRecordId(Long recordId, InternalTenantContext context) throws AccountApiException;

public UUID getByRecordId(Long recordId, InternalTenantContext context) throws AccountApiException;
}
Expand Up @@ -20,14 +20,14 @@
import java.util.Map;
import java.util.UUID;

import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.entitlement.api.Entitlement;
import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;

// Wrapper object to save on DAO calls
public interface AccountEntitlements {

public Account getAccount();
public ImmutableAccountData getAccount();

// Map bundle id -> bundle
public Map<UUID, SubscriptionBaseBundle> getBundles();
Expand Down
Expand Up @@ -20,13 +20,13 @@
import java.util.Map;
import java.util.UUID;

import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;

// Wrapper object to save on DAO calls
public interface AccountEventsStreams {

public Account getAccount();
public ImmutableAccountData getAccount();

// Map bundle id -> bundle
public Map<UUID, SubscriptionBaseBundle> getBundles();
Expand Down
Expand Up @@ -34,10 +34,6 @@

public interface BillingEvent extends Comparable<BillingEvent> {

/**
* @return the account that this billing event is associated with
*/
Account getAccount();

/**
* @return the billCycleDay in the account timezone as seen for that subscription at that time
Expand Down
Expand Up @@ -16,7 +16,7 @@

package org.killbill.billing.overdue;

import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.overdue.api.OverdueApiException;
import org.killbill.billing.overdue.api.OverdueState;
import org.killbill.billing.overdue.config.api.BillingState;
Expand All @@ -26,12 +26,12 @@

public interface OverdueInternalApi {

public OverdueState refreshOverdueStateFor(Account overdueable, CallContext context) throws OverdueException, OverdueApiException;
public OverdueState refreshOverdueStateFor(ImmutableAccountData overdueable, CallContext context) throws OverdueException, OverdueApiException;

public void setOverrideBillingStateForAccount(Account overdueable, BillingState state, CallContext context) throws OverdueException;
public void setOverrideBillingStateForAccount(ImmutableAccountData overdueable, BillingState state, CallContext context) throws OverdueException;

public OverdueState getOverdueStateFor(Account overdueable, TenantContext context) throws OverdueException;
public OverdueState getOverdueStateFor(ImmutableAccountData overdueable, TenantContext context) throws OverdueException;

public BillingState getBillingStateFor(Account overdueable, TenantContext context) throws OverdueException;
public BillingState getBillingStateFor(ImmutableAccountData overdueable, TenantContext context) throws OverdueException;

}
Expand Up @@ -26,9 +26,9 @@
import org.joda.time.LocalDate;
import org.killbill.billing.ErrorCode;
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.AccountInternalApi;
import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingActionPolicy;
Expand Down Expand Up @@ -303,7 +303,7 @@ public Void doCall(final EntitlementApi entitlementApi, final EntitlementContext
}

final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getBundleFromId(bundleId, contextWithValidAccountRecordId);
final Account account = accountApi.getAccountById(bundle.getAccountId(), contextWithValidAccountRecordId);
final ImmutableAccountData account = accountApi.getImmutableAccountDataById(bundle.getAccountId(), contextWithValidAccountRecordId);
final SubscriptionBase baseSubscription = subscriptionBaseInternalApi.getBaseSubscription(bundleId, contextWithValidAccountRecordId);
final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), baseSubscription.getStartDate(), contextWithValidAccountRecordId);

Expand Down Expand Up @@ -358,7 +358,7 @@ public Void doCall(final EntitlementApi entitlementApi, final EntitlementContext
try {
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, context);
final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getBundleFromId(bundleId, contextWithValidAccountRecordId);
final Account account = accountApi.getAccountById(bundle.getAccountId(), contextWithValidAccountRecordId);
final ImmutableAccountData account = accountApi.getImmutableAccountDataById(bundle.getAccountId(), contextWithValidAccountRecordId);
final SubscriptionBase baseSubscription = subscriptionBaseInternalApi.getBaseSubscription(bundleId, contextWithValidAccountRecordId);

final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), baseSubscription.getStartDate(), contextWithValidAccountRecordId);
Expand Down
Expand Up @@ -18,12 +18,10 @@

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
import org.joda.time.LocalDate;

import org.killbill.billing.account.api.Account;
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.InternalTenantContext;
import org.killbill.clock.Clock;
import org.killbill.clock.ClockUtil;
Expand All @@ -40,7 +38,7 @@ public EntitlementDateHelper(final AccountInternalApi accountApi, final Clock cl

public DateTime fromLocalDateAndReferenceTime(final LocalDate requestedDate, final DateTime referenceDateTime, final InternalTenantContext callContext) throws EntitlementApiException {
try {
final Account account = accountApi.getAccountByRecordId(callContext.getAccountRecordId(), callContext);
final ImmutableAccountData account = accountApi.getImmutableAccountDataByRecordId(callContext.getAccountRecordId(), callContext);
return ClockUtil.computeDateTimeWithUTCReferenceTime(requestedDate, referenceDateTime.toDateTime(DateTimeZone.UTC).toLocalTime(), account.getTimeZone(), clock);
} catch (AccountApiException e) {
throw new EntitlementApiException(e);
Expand Down
Expand Up @@ -20,7 +20,7 @@
import java.util.Map;
import java.util.UUID;

import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.entitlement.AccountEntitlements;
import org.killbill.billing.entitlement.AccountEventsStreams;
import org.killbill.billing.entitlement.api.Entitlement;
Expand All @@ -37,7 +37,7 @@ public DefaultAccountEntitlements(final AccountEventsStreams accountEventsStream
}

@Override
public Account getAccount() {
public ImmutableAccountData getAccount() {
return accountEventsStreams.getAccount();
}

Expand Down

0 comments on commit 5bec17d

Please sign in to comment.