killbill-memoizeit

payment: implement pagination for getPaymentMethods Signed-off-by:

10/23/2013 10:50:12 PM

Changes

.idea/libraries/Maven__com_ning_billing_killbill_api_0_6_7.xml 13(+0 -13)

.idea/libraries/Maven__com_ning_billing_plugin_killbill_plugin_api_notification_0_4_0.xml 13(+0 -13)

.idea/libraries/Maven__com_ning_billing_plugin_killbill_plugin_api_payment_0_4_0.xml 13(+0 -13)

pom.xml 2(+1 -1)

Details

diff --git a/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java b/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
index 5f434c7..486057d 100644
--- a/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
@@ -26,6 +26,7 @@ import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.AccountEmail;
+import com.ning.billing.account.api.AccountInternalApi;
 import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.account.api.DefaultAccountEmail;
 import com.ning.billing.account.dao.AccountDao;
@@ -33,11 +34,13 @@ import com.ning.billing.account.dao.AccountEmailModelDao;
 import com.ning.billing.account.dao.AccountModelDao;
 import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.callcontext.InternalTenantContext;
-import com.ning.billing.account.api.AccountInternalApi;
+import com.ning.billing.util.entity.DefaultPagination;
+import com.ning.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 {
 
@@ -64,6 +67,21 @@ public class DefaultAccountInternalApi implements AccountInternalApi {
     }
 
     @Override
+    public Pagination<Account> getAccounts(final Long offset, final Long limit, final InternalTenantContext context) {
+        final Pagination<AccountModelDao> accountModelDaos = accountDao.get(offset, limit, context);
+        return new DefaultPagination<Account>(accountModelDaos,
+                                              limit,
+                                              Iterators.<AccountModelDao, Account>transform(accountModelDaos.iterator(),
+                                                                                            new Function<AccountModelDao, Account>() {
+                                                                                                @Override
+                                                                                                public Account apply(final AccountModelDao input) {
+                                                                                                    return new DefaultAccount(input);
+                                                                                                }
+                                                                                            }));
+
+    }
+
+    @Override
     public void updateAccount(final String externalKey, final AccountData accountData,
                               final InternalCallContext context) throws AccountApiException {
         final Account currentAccount = getAccountByKey(externalKey, context);
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountInternalApi.java b/api/src/main/java/com/ning/billing/account/api/AccountInternalApi.java
index 02ee51e..684ca30 100644
--- a/api/src/main/java/com/ning/billing/account/api/AccountInternalApi.java
+++ b/api/src/main/java/com/ning/billing/account/api/AccountInternalApi.java
@@ -21,6 +21,7 @@ import java.util.UUID;
 
 import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.entity.Pagination;
 
 public interface AccountInternalApi {
 
@@ -30,6 +31,8 @@ public interface AccountInternalApi {
 
     public Account getAccountByRecordId(Long recordId, InternalTenantContext context) throws AccountApiException;
 
+    public Pagination<Account> getAccounts(Long offset, Long limit, InternalTenantContext context);
+
     public void updateAccount(String key, AccountData accountData, InternalCallContext context) throws AccountApiException;
 
     public List<AccountEmail> getEmails(UUID accountId, InternalTenantContext context);
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java
index 1177d01..1cdc630 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java
@@ -98,6 +98,28 @@ public class PaymentMethodResource extends JaxRsResourceBase {
     }
 
     @GET
+    @Path("/" + PAGINATION)
+    @Produces(APPLICATION_JSON)
+    public Response getPaymentMethods(@QueryParam(QUERY_SEARCH_OFFSET) @DefaultValue("0") final Long offset,
+                                      @QueryParam(QUERY_SEARCH_LIMIT) @DefaultValue("100") final Long limit,
+                                      @QueryParam(QUERY_PAYMENT_METHOD_PLUGIN_NAME) final String pluginName,
+                                      @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
+        final TenantContext tenantContext = context.createContext(request);
+
+        final Pagination<PaymentMethod> paymentMethods;
+        final Map<String, String> nextUriParams = new HashMap<String, String>();
+        if (Strings.isNullOrEmpty(pluginName)) {
+            paymentMethods = paymentApi.getPaymentMethods(offset, limit, tenantContext);
+        } else {
+            paymentMethods = paymentApi.getPaymentMethods(offset, limit, pluginName, tenantContext);
+            nextUriParams.put(QUERY_PAYMENT_METHOD_PLUGIN_NAME, pluginName);
+        }
+
+        final URI nextPageUri = uriBuilder.nextPage(PaymentMethodResource.class, "getPaymentMethods", paymentMethods.getNextOffset(), limit, nextUriParams);
+        return buildStreamingPaymentMethodsResponse(paymentMethods, nextPageUri, tenantContext);
+    }
+
+    @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
     public Response searchPaymentMethods(@PathParam("searchKey") final String searchKey,
@@ -116,7 +138,10 @@ public class PaymentMethodResource extends JaxRsResourceBase {
         }
 
         final URI nextPageUri = uriBuilder.nextPage(AccountResource.class, "searchPaymentMethods", paymentMethods.getNextOffset(), limit, ImmutableMap.<String, String>of());
+        return buildStreamingPaymentMethodsResponse(paymentMethods, nextPageUri, tenantContext);
+    }
 
+    private Response buildStreamingPaymentMethodsResponse(final Pagination<PaymentMethod> paymentMethods, final URI nextPageUri, final TenantContext tenantContext) {
         final Map<UUID, Account> accounts = new HashMap<UUID, Account>();
         final StreamingOutput json = new StreamingOutput() {
             @Override
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index aeb556b..579ded0 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -26,13 +26,17 @@ import java.util.UUID;
 
 import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
+import com.ning.billing.callcontext.DefaultCallContext;
 import com.ning.billing.callcontext.InternalCallContext;
+import com.ning.billing.clock.Clock;
 import com.ning.billing.payment.core.PaymentMethodProcessor;
 import com.ning.billing.payment.core.PaymentProcessor;
 import com.ning.billing.payment.core.RefundProcessor;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.entity.Pagination;
 
 import com.google.common.collect.ImmutableMap;
@@ -44,15 +48,18 @@ public class DefaultPaymentApi implements PaymentApi {
     private final PaymentProcessor paymentProcessor;
     private final RefundProcessor refundProcessor;
     private final InternalCallContextFactory internalCallContextFactory;
+    private final Clock clock;
 
     @Inject
     public DefaultPaymentApi(final PaymentMethodProcessor methodProcessor,
                              final PaymentProcessor paymentProcessor,
                              final RefundProcessor refundProcessor,
+                             final Clock clock,
                              final InternalCallContextFactory internalCallContextFactory) {
         this.methodProcessor = methodProcessor;
         this.paymentProcessor = paymentProcessor;
         this.refundProcessor = refundProcessor;
+        this.clock = clock;
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
@@ -174,6 +181,20 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
+    public Pagination<PaymentMethod> getPaymentMethods(final Long offset, final Long limit, final TenantContext context) {
+        // Lame, but required
+        final CallContext callContext = new DefaultCallContext(context.getTenantId(), DefaultPaymentApi.class.toString(), CallOrigin.EXTERNAL, UserType.CUSTOMER, UUID.randomUUID(), clock);
+        return methodProcessor.getPaymentMethods(offset, limit, context, callContext, internalCallContextFactory.createInternalTenantContext(context));
+    }
+
+    @Override
+    public Pagination<PaymentMethod> getPaymentMethods(final Long offset, final Long limit, final String pluginName, final TenantContext context) throws PaymentApiException {
+        // Lame, but required
+        final CallContext callContext = new DefaultCallContext(context.getTenantId(), DefaultPaymentApi.class.toString(), CallOrigin.EXTERNAL, UserType.CUSTOMER, UUID.randomUUID(), clock);
+        return methodProcessor.getPaymentMethods(offset, limit, pluginName, context, callContext, internalCallContextFactory.createInternalTenantContext(context));
+    }
+
+    @Override
     public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final TenantContext context) {
         return methodProcessor.searchPaymentMethods(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContext(context));
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
index 503df8b..f720133 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
@@ -53,6 +53,8 @@ import com.ning.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
 import com.ning.billing.payment.provider.DefaultPaymentMethodInfoPlugin;
 import com.ning.billing.payment.provider.ExternalPaymentProviderPlugin;
 import com.ning.billing.tag.TagInternalApi;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.dao.NonEntityDao;
 import com.ning.billing.util.entity.DefaultPagination;
 import com.ning.billing.util.entity.Pagination;
@@ -157,6 +159,74 @@ public class PaymentMethodProcessor extends ProcessorBase {
         return new DefaultPaymentMethod(paymentMethodModelDao, paymentMethodPlugin);
     }
 
+    public Pagination<PaymentMethod> getPaymentMethods(final Long offset, final Long limit, final TenantContext tenantContext, final CallContext callContext, final InternalTenantContext internalTenantContext) {
+        // Note that we cannot easily do streaming here, since we would have to rely on the statistics
+        // returned by the Pagination objects from the plugins and we probably don't want to do that (if
+        // one plugin gets it wrong, it may starve the others).
+        final List<PaymentMethod> allResults = new LinkedList<PaymentMethod>();
+
+        // Search in all plugins (we treat the full set of results as a union with respect to offset/limit)
+        for (final String pluginName : getAvailablePlugins()) {
+            try {
+                final Pagination<PaymentMethod> paymentMethods = getPaymentMethods(0L, Long.MAX_VALUE, pluginName, tenantContext, callContext, internalTenantContext);
+                allResults.addAll(ImmutableList.<PaymentMethod>copyOf(paymentMethods));
+                if (allResults.size() > offset + limit) {
+                    break;
+                }
+            } catch (PaymentApiException e) {
+                log.warn("Error while searching plugin " + pluginName, e);
+                // Non-fatal, continue to search other plugins
+            }
+        }
+
+        return DefaultPagination.<PaymentMethod>build(offset, limit, allResults);
+    }
+
+    public Pagination<PaymentMethod> getPaymentMethods(final Long offset, final Long limit, final String pluginName, final TenantContext tenantContext, final CallContext callContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
+        final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
+        final List<PaymentMethodPlugin> paymentMethods = new LinkedList<PaymentMethodPlugin>();
+
+        // Find all payment methods for all accounts
+        final Pagination<Account> accounts = accountInternalApi.getAccounts(0L, Long.MAX_VALUE, internalTenantContext);
+        for (final Account account : accounts) {
+            try {
+                final List<PaymentMethodInfoPlugin> paymentMethodInfos = pluginApi.getPaymentMethods(account.getId(), true, callContext);
+                for (final PaymentMethodInfoPlugin paymentMethodInfoPlugin : paymentMethodInfos) {
+                    final PaymentMethodPlugin paymentMethodDetail = pluginApi.getPaymentMethodDetail(account.getId(), paymentMethodInfoPlugin.getPaymentMethodId(), tenantContext);
+                    paymentMethods.add(paymentMethodDetail);
+                    if (paymentMethods.size() > offset + limit) {
+                        break;
+                    }
+                }
+            } catch (PaymentPluginApiException e) {
+                throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_GET_PAYMENT_METHODS, pluginName);
+            }
+        }
+
+        return new DefaultPagination<PaymentMethod>(DefaultPagination.<PaymentMethodPlugin>build(offset, limit, paymentMethods),
+                                                    limit,
+                                                    Iterators.<PaymentMethod>filter(Iterators.<PaymentMethodPlugin, PaymentMethod>transform(paymentMethods.iterator(),
+                                                                                                                                            new Function<PaymentMethodPlugin, PaymentMethod>() {
+                                                                                                                                                @Override
+                                                                                                                                                public PaymentMethod apply(final PaymentMethodPlugin paymentMethodPlugin) {
+                                                                                                                                                    if (paymentMethodPlugin.getKbPaymentMethodId() == null) {
+                                                                                                                                                        // Garbage from the plugin?
+                                                                                                                                                        log.debug("Plugin {} returned a payment method without a kbPaymentMethodId", pluginName);
+                                                                                                                                                        return null;
+                                                                                                                                                    }
+
+                                                                                                                                                    final PaymentMethodModelDao paymentMethodModelDao = paymentDao.getPaymentMethodIncludedDeleted(paymentMethodPlugin.getKbPaymentMethodId(), internalTenantContext);
+                                                                                                                                                    if (paymentMethodModelDao == null) {
+                                                                                                                                                        log.warn("Unable to find payment method id " + paymentMethodPlugin.getKbPaymentMethodId() + " present in plugin " + pluginName);
+                                                                                                                                                        return null;
+                                                                                                                                                    }
+
+                                                                                                                                                    return new DefaultPaymentMethod(paymentMethodModelDao, paymentMethodPlugin);
+                                                                                                                                                }
+                                                                                                                                            }),
+                                                                                    Predicates.<PaymentMethod>notNull()));
+    }
+
     public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final InternalTenantContext internalTenantContext) {
         // Note that we cannot easily do streaming here, since we would have to rely on the statistics
         // returned by the Pagination objects from the plugins and we probably don't want to do that (if

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 68be740..1f51d19 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.5.0-SNAPSHOT</version>
+        <version>0.5.1</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.6.18-SNAPSHOT</version>