/*
* Copyright 2010-2013 Ning, Inc.
*
* Ning 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 com.ning.billing.entitlement.api;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.inject.Inject;
import org.joda.time.DateTimeZone;
import com.ning.billing.ErrorCode;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.clock.Clock;
import com.ning.billing.entitlement.block.BlockingChecker;
import com.ning.billing.entitlement.dao.BlockingStateDao;
import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
import com.ning.billing.util.callcontext.InternalCallContextFactory;
import com.ning.billing.util.callcontext.InternalTenantContext;
import com.ning.billing.util.callcontext.TenantContext;
import com.ning.billing.util.svcapi.account.AccountInternalApi;
import com.ning.billing.util.svcapi.subscription.SubscriptionBaseInternalApi;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
public class DefaultSubscriptionApi implements SubscriptionApi {
private final SubscriptionBaseInternalApi subscriptionInternalApi;
private final EntitlementApi entitlementApi;
private final BlockingChecker checker;
private final BlockingStateDao blockingStateDao;
private final EntitlementDateHelper dateHelper;
private final Clock clock;
private final InternalCallContextFactory internalCallContextFactory;
private final AccountInternalApi accountApi;
@Inject
public DefaultSubscriptionApi(final SubscriptionBaseInternalApi subscriptionInternalApi, final EntitlementApi entitlementApi, final BlockingChecker checker, final BlockingStateDao blockingStateDao, final AccountInternalApi accountApi, final Clock clock, final InternalCallContextFactory internalCallContextFactory) {
this.subscriptionInternalApi = subscriptionInternalApi;
this.entitlementApi = entitlementApi;
this.accountApi = accountApi;
this.checker = checker;
this.blockingStateDao = blockingStateDao;
this.dateHelper = new EntitlementDateHelper(accountApi, clock);
;
this.clock = clock;
this.internalCallContextFactory = internalCallContextFactory;
}
@Override
public Subscription getSubscriptionForEntitlementId(final UUID entitlementId, final TenantContext context) throws SubscriptionApiException {
try {
final Entitlement entitlement = entitlementApi.getEntitlementForId(entitlementId, context);
final InternalTenantContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(entitlement.getAccountId(), context);
return fromEntitlement(entitlement, contextWithValidAccountRecordId);
} catch (EntitlementApiException e) {
throw new SubscriptionApiException(e);
}
}
@Override
public SubscriptionBundle getSubscriptionBundle(final UUID bundleId, final TenantContext context) throws SubscriptionApiException {
try {
final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForBundle(bundleId, context);
if (entitlements.isEmpty()) {
throw new SubscriptionApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_ID, bundleId);
}
return getSubscriptionBundleFromEntitlements(bundleId, entitlements, context);
} catch (EntitlementApiException e) {
throw new SubscriptionApiException(e);
} catch (SubscriptionBaseApiException e) {
throw new SubscriptionApiException(e);
} catch (AccountApiException e) {
throw new SubscriptionApiException(e);
}
}
@Override
public SubscriptionBundle getSubscriptionBundleForAccountIdAndExternalKey(final UUID accountId, final String externalKey, final TenantContext context) throws SubscriptionApiException {
try {
// If active bundle exists on this account, this is what we are looking for.
try {
final SubscriptionBundle activeBundleForkey = getActiveSubscriptionBundleForExternalKey(externalKey, context);
if (activeBundleForkey.getAccountId().equals(accountId)) {
return activeBundleForkey;
}
} catch (SubscriptionApiException ignore) {
}
// If not return first cancelled bundle if this is exists
final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForAccountIdAndExternalKey(accountId, externalKey, context);
if (entitlements.isEmpty()) {
throw new SubscriptionApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_KEY, externalKey);
}
return getSubscriptionBundleFromEntitlements(entitlements.get(0).getBundleId(), entitlements, context);
} catch (EntitlementApiException e) {
throw new SubscriptionApiException(e);
} catch (SubscriptionBaseApiException e) {
throw new SubscriptionApiException(e);
} catch (AccountApiException e) {
throw new SubscriptionApiException(e);
}
}
@Override
public SubscriptionBundle getActiveSubscriptionBundleForExternalKey(final String externalKey,
final TenantContext context) throws SubscriptionApiException {
final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
try {
final SubscriptionBaseBundle baseBundle = subscriptionInternalApi.getActiveBundleForKey(externalKey, internalContext);
final List<Entitlement> allEntitlementsForBundle = entitlementApi.getAllEntitlementsForBundle(baseBundle.getId(), context);
return getSubscriptionBundleFromEntitlements(baseBundle.getId(), allEntitlementsForBundle, context);
} catch (SubscriptionBaseApiException e) {
throw new SubscriptionApiException(e);
} catch (EntitlementApiException e) {
throw new SubscriptionApiException(e);
} catch (AccountApiException e) {
throw new SubscriptionApiException(e);
}
}
@Override
public List<SubscriptionBundle> getSubscriptionBundlesForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
try {
final List<SubscriptionBaseBundle> baseBundles = subscriptionInternalApi.getBundlesForKey(externalKey, internalContext);
final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(baseBundles.size());
for (SubscriptionBaseBundle cur : baseBundles) {
final List<Entitlement> allEntitlementsForBundle = entitlementApi.getAllEntitlementsForBundle(cur.getId(), context);
final SubscriptionBundle bundle = getSubscriptionBundleFromEntitlements(cur.getId(), allEntitlementsForBundle, context);
result.add(bundle);
}
return result;
} catch (SubscriptionBaseApiException e) {
throw new SubscriptionApiException(e);
} catch (EntitlementApiException e) {
throw new SubscriptionApiException(e);
} catch (AccountApiException e) {
throw new SubscriptionApiException(e);
}
}
@Override
public List<SubscriptionBundle> getSubscriptionBundlesForAccountId(final UUID accountId,
final TenantContext context) throws SubscriptionApiException {
try {
final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForAccountId(accountId, context);
if (entitlements.isEmpty()) {
return Collections.emptyList();
}
return getSubscriptionBundles(entitlements, context);
} catch (EntitlementApiException e) {
throw new SubscriptionApiException(e);
} catch (SubscriptionBaseApiException e) {
throw new SubscriptionApiException(e);
} catch (AccountApiException e) {
throw new SubscriptionApiException(e);
}
}
private List<SubscriptionBundle> getSubscriptionBundles(final List<Entitlement> entitlements, final TenantContext context) throws SubscriptionBaseApiException, AccountApiException {
final ListMultimap<UUID, Entitlement> perBundleEntitlements = LinkedListMultimap.create();
for (Entitlement cur : entitlements) {
perBundleEntitlements.put(cur.getBundleId(), cur);
}
final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(perBundleEntitlements.keySet().size());
for (UUID bundleId : perBundleEntitlements.keySet()) {
final List<Entitlement> allEntitlementsForBundle = perBundleEntitlements.get(bundleId);
final SubscriptionBundle bundle = getSubscriptionBundleFromEntitlements(bundleId, allEntitlementsForBundle, context);
result.add(bundle);
}
return result;
}
private Subscription fromEntitlement(final Entitlement entitlement, final InternalTenantContext internalTenantContext) {
final List<BlockingState> states = blockingStateDao.getBlockingState(entitlement.getId(), internalTenantContext);
final Subscription result = new DefaultSubscription((DefaultEntitlement) entitlement, states);
return result;
}
private SubscriptionBundle getSubscriptionBundleFromEntitlements(final UUID bundleId, final List<Entitlement> entitlements, final TenantContext context) throws SubscriptionBaseApiException, AccountApiException {
final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
final SubscriptionBaseBundle baseBundle = subscriptionInternalApi.getBundleFromId(bundleId, internalTenantContext);
final List<Subscription> subscriptions = new ArrayList<Subscription>(entitlements.size());
subscriptions.addAll(Collections2.transform(entitlements, new Function<Entitlement, Subscription>() {
@Override
public Subscription apply(final Entitlement input) {
return fromEntitlement(input, internalTenantContext);
}
}));
final Account account = accountApi.getAccountById(baseBundle.getAccountId(), internalTenantContext);
final InternalTenantContext internalTenantContextWithAccountRecordId = internalCallContextFactory.createInternalTenantContext(account.getId(), context);
final DateTimeZone accountTimeZone = account.getTimeZone();
final List<BlockingState> allBlockingStatesPerAccountRecordId = blockingStateDao.getBlockingAllForAccountRecordId(internalTenantContextWithAccountRecordId);
final Set<UUID> allEntitlementIds = new HashSet<UUID>(Collections2.<Entitlement, UUID>transform(entitlements, new Function<Entitlement, UUID>() {
@Override
public UUID apply(final Entitlement input) {
return input.getId();
}
}));
final List<BlockingState> filteredBlockingStates = new LinkedList<BlockingState>(Collections2.filter(allBlockingStatesPerAccountRecordId, new Predicate<BlockingState>() {
@Override
public boolean apply(final BlockingState input) {
return input.getType() == BlockingStateType.ACCOUNT ||
(input.getType() == BlockingStateType.SUBSCRIPTION_BUNDLE && input.getBlockedId().equals(bundleId)) ||
(input.getType() == BlockingStateType.SUBSCRIPTION && allEntitlementIds.contains(input.getBlockedId()));
}
}));
final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, account.getId(), bundleId, baseBundle.getExternalKey(), entitlements, filteredBlockingStates);
final DefaultSubscriptionBundle bundle = new DefaultSubscriptionBundle(bundleId, baseBundle.getAccountId(), baseBundle.getExternalKey(), subscriptions, timeline, baseBundle.getOriginalCreatedDate() , baseBundle.getCreatedDate(), baseBundle.getUpdatedDate());
return bundle;
}
}