killbill-memoizeit
Changes
NEWS 5(+5 -0)
Details
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 171d337..e8fec16 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -134,6 +134,25 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
}
@Override
+ public Pagination<Invoice> searchInvoices(final String searchKey, final Long offset, final Long limit, final TenantContext context) {
+ return getEntityPaginationNoException(limit,
+ new SourcePaginationBuilder<InvoiceModelDao, AccountApiException>() {
+ @Override
+ public Pagination<InvoiceModelDao> build() {
+ // Invoices will be shallow, i.e. won't contain items nor payments
+ return dao.searchInvoices(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+ }
+ },
+ new Function<InvoiceModelDao, Invoice>() {
+ @Override
+ public Invoice apply(final InvoiceModelDao invoiceModelDao) {
+ return new DefaultInvoice(invoiceModelDao);
+ }
+ }
+ );
+ }
+
+ @Override
public BigDecimal getAccountBalance(final UUID accountId, final TenantContext context) {
final BigDecimal result = dao.getAccountBalance(accountId, internalCallContextFactory.createInternalTenantContext(accountId, context));
return result == null ? BigDecimal.ZERO : result;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index 754b74d..441eba2 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -17,6 +17,7 @@
package com.ning.billing.invoice.dao;
import java.math.BigDecimal;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -45,6 +46,8 @@ import com.ning.billing.invoice.notification.NextBillingDatePoster;
import com.ning.billing.util.cache.CacheControllerDispatcher;
import com.ning.billing.util.callcontext.InternalCallContextFactory;
import com.ning.billing.util.dao.NonEntityDao;
+import com.ning.billing.util.entity.Pagination;
+import com.ning.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.PaginationIteratorBuilder;
import com.ning.billing.util.entity.dao.EntityDaoBase;
import com.ning.billing.util.entity.dao.EntitySqlDao;
import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
@@ -248,6 +251,21 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
@Override
+ public Pagination<InvoiceModelDao> searchInvoices(final String searchKey, final Long offset, final Long limit, final InternalTenantContext context) {
+ return paginationHelper.getPagination(InvoiceSqlDao.class,
+ new PaginationIteratorBuilder<InvoiceModelDao, Invoice, InvoiceSqlDao>() {
+ @Override
+ public Iterator<InvoiceModelDao> build(final InvoiceSqlDao invoiceSqlDao, final Long limit) {
+ return invoiceSqlDao.searchInvoices(searchKey, offset, limit, context);
+ }
+ },
+ offset,
+ limit,
+ context);
+
+ }
+
+ @Override
public BigDecimal getAccountBalance(final UUID accountId, final InternalTenantContext context) {
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<BigDecimal>() {
@Override
@@ -285,7 +303,6 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
});
}
-
@Override
public UUID getInvoiceIdByPaymentId(final UUID paymentId, final InternalTenantContext context) {
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<UUID>() {
@@ -540,7 +557,6 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
transInvoiceItemDao.create(externalCharge, context);
-
cbaDao.doCBAComplexity(accountId, entitySqlDaoWrapperFactory, context);
// Notify the bus since the balance of the invoice changed
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
index 8f2c668..73f1185 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
@@ -31,6 +31,7 @@ import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.util.entity.Pagination;
import com.ning.billing.util.entity.dao.EntityDao;
public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceApiException> {
@@ -46,6 +47,8 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
List<InvoiceModelDao> getInvoicesBySubscription(UUID subscriptionId, InternalTenantContext context);
+ public Pagination<InvoiceModelDao> searchInvoices(String searchKey, Long offset, Long limit, InternalTenantContext context);
+
UUID getInvoiceIdByPaymentId(UUID paymentId, InternalTenantContext context);
List<InvoicePaymentModelDao> getInvoicePayments(UUID paymentId, InternalTenantContext context);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
index 90099ac..a0824db 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
@@ -16,15 +16,17 @@
package com.ning.billing.invoice.dao;
+import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import org.skife.jdbi.v2.sqlobject.Bind;
import org.skife.jdbi.v2.sqlobject.BindBean;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.customizers.FetchSize;
-import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.util.entity.dao.EntitySqlDao;
import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
@@ -36,6 +38,15 @@ public interface InvoiceSqlDao extends EntitySqlDao<InvoiceModelDao, Invoice> {
@BindBean final InternalTenantContext context);
@SqlQuery
+ // Magic value to force MySQL to stream from the database
+ // See http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-implementation-notes.html (ResultSet)
+ @FetchSize(Integer.MIN_VALUE)
+ public Iterator<InvoiceModelDao> searchInvoices(@Bind("searchKey") final String searchKey,
+ @Bind("offset") final Long offset,
+ @Bind("rowCount") final Long rowCount,
+ @BindBean final InternalTenantContext context);
+
+ @SqlQuery
UUID getInvoiceIdByPaymentId(@Bind("paymentId") final String paymentId,
@BindBean final InternalTenantContext context);
}
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
index 5fd95ce..304da69 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -36,6 +36,23 @@ getInvoicesBySubscription() ::= <<
;
>>
+searchInvoices() ::= <<
+select SQL_CALC_FOUND_ROWS
+<allTableFields("t.")>
+from <tableName()> t
+where 1 = 1
+and (
+ <idField("t.")> = :searchKey
+ or <recordIdField("t.")> = :searchKey
+ or t.account_id = :searchKey
+ or t.currency = :searchKey
+)
+<AND_CHECK_TENANT("t.")>
+order by <recordIdField("t.")> ASC
+limit :offset, :rowCount
+;
+>>
+
getInvoiceIdByPaymentId() ::= <<
SELECT i.id
FROM <tableName()> i, invoice_payments ip
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
index 5006858..6ce801b 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
@@ -156,6 +156,21 @@ public class MockInvoiceDao extends MockEntityDaoBase<InvoiceModelDao, Invoice,
}
@Override
+ public Pagination<InvoiceModelDao> searchInvoices(final String searchKey, final Long offset, final Long limit, final InternalTenantContext context) {
+ final List<InvoiceModelDao> results = new LinkedList<InvoiceModelDao>();
+ for (final InvoiceModelDao invoice : getAll(context)) {
+ if (invoice.getId().toString().equals(searchKey) ||
+ invoice.getAccountId().toString().equals(searchKey) ||
+ invoice.getInvoiceNumber().toString().equals(searchKey) ||
+ invoice.getCurrency().toString().equals(searchKey)) {
+ results.add(invoice);
+ }
+ }
+
+ return DefaultPagination.<InvoiceModelDao>build(offset, limit, results);
+ }
+
+ @Override
public void test(final InternalTenantContext context) {
}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
index f034473..ed14584 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
@@ -51,6 +51,7 @@ import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.api.SubscriptionApiException;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.InvoiceItem;
@@ -186,6 +187,35 @@ public class InvoiceResource extends JaxRsResourceBase {
nextPageUri);
}
+ @GET
+ @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
+ @Produces(APPLICATION_JSON)
+ public Response searchInvoices(@PathParam("searchKey") final String searchKey,
+ @QueryParam(QUERY_SEARCH_OFFSET) @DefaultValue("0") final Long offset,
+ @QueryParam(QUERY_SEARCH_LIMIT) @DefaultValue("100") final Long limit,
+ @QueryParam(QUERY_INVOICE_WITH_ITEMS) @DefaultValue("false") final Boolean withItems,
+ @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
+ @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException {
+ final TenantContext tenantContext = context.createContext(request);
+ final Pagination<Invoice> invoices = invoiceApi.searchInvoices(searchKey, offset, limit, tenantContext);
+ final URI nextPageUri = uriBuilder.nextPage(InvoiceResource.class, "searchInvoices", invoices.getNextOffset(), limit, ImmutableMap.<String, String>of("searchKey", searchKey,
+ QUERY_INVOICE_WITH_ITEMS, withItems.toString(),
+ QUERY_AUDIT, auditMode.getLevel().toString()));
+ final AtomicReference<Map<UUID, AccountAuditLogs>> accountsAuditLogs = new AtomicReference<Map<UUID, AccountAuditLogs>>(new HashMap<UUID, AccountAuditLogs>());
+ return buildStreamingPaginationResponse(invoices,
+ new Function<Invoice, InvoiceJson>() {
+ @Override
+ public InvoiceJson apply(final Invoice invoice) {
+ // Cache audit logs per account
+ if (accountsAuditLogs.get().get(invoice.getAccountId()) == null) {
+ accountsAuditLogs.get().put(invoice.getAccountId(), auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext));
+ }
+ return new InvoiceJson(invoice, withItems, accountsAuditLogs.get().get(invoice.getAccountId()));
+ }
+ },
+ nextPageUri);
+ }
+
@POST
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
NEWS 5(+5 -0)
diff --git a/NEWS b/NEWS
index 28c4313..36ef7f7 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+0.8.12
+ Implement bundles pagination and search APIs
+ Implement invoices search API
+ https://github.com/killbill/killbill/issues/154
+
0.8.11
[SECURITY] Fix SQL injection in search APIs
Fix bug when retrieving refund information from plugins
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java b/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
index 6d904a2..0622059 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
@@ -450,7 +450,7 @@ public class TestInvoice extends TestJaxrsBase {
assertEquals(adjustedInvoice.getBalance().compareTo(adjustedInvoiceBalance), 0);
}
- @Test(groups = "slow", description = "Can paginate through all invoices")
+ @Test(groups = "slow", description = "Can paginate and search through all invoices")
public void testInvoicesPagination() throws Exception {
createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
@@ -462,6 +462,13 @@ public class TestInvoice extends TestJaxrsBase {
final Invoices allInvoices = killBillClient.getInvoices();
Assert.assertEquals(allInvoices.size(), 5);
+ for (final Invoice invoice : allInvoices) {
+ Assert.assertEquals(killBillClient.searchInvoices(invoice.getInvoiceId().toString()).size(), 1);
+ Assert.assertEquals(killBillClient.searchInvoices(invoice.getAccountId().toString()).size(), 5);
+ Assert.assertEquals(killBillClient.searchInvoices(invoice.getInvoiceNumber().toString()).size(), 1);
+ Assert.assertEquals(killBillClient.searchInvoices(invoice.getCurrency().toString()).size(), 5);
+ }
+
Invoices page = killBillClient.getInvoices(0L, 1L);
for (int i = 0; i < 5; i++) {
Assert.assertNotNull(page);