killbill-aplcache
Changes
subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java 11(+3 -8)
Details
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index 7742de0..bf84173 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -361,7 +361,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
existingInvoiceMetadata = existingInvoiceMetadataOrNull;
}
- final List<InvoiceItemModelDao> createdInvoiceItems = new LinkedList<InvoiceItemModelDao>();
+ final List<InvoiceItemModelDao> invoiceItemsToCreate = new LinkedList<InvoiceItemModelDao>();
for (final InvoiceModelDao invoiceModelDao : invoices) {
invoiceByInvoiceId.put(invoiceModelDao.getId(), invoiceModelDao);
final boolean isNotShellInvoice = invoiceIdsReferencedFromItems.remove(invoiceModelDao.getId());
@@ -384,7 +384,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
// Because of AUTO_INVOICING_REUSE_DRAFT we expect an invoice were items might already exist.
// Also for ALLOWED_INVOICE_ITEM_TYPES, we expect plugins to potentially modify the amount
if (existingInvoiceItem == null) {
- createdInvoiceItems.add(createInvoiceItemFromTransaction(transInvoiceItemSqlDao, invoiceItemModelDao, context));
+ invoiceItemsToCreate.add(invoiceItemModelDao);
allInvoiceIds.add(invoiceItemModelDao.getInvoiceId());
} else if (InvoicePluginDispatcher.ALLOWED_INVOICE_ITEM_TYPES.contains(invoiceItemModelDao.getType()) &&
// The restriction on the amount is to deal with https://github.com/killbill/killbill/issues/993 - and esnure that duplicate
@@ -399,7 +399,6 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
final boolean wasInvoiceCreatedOrCommitted = createdInvoiceIds.contains(invoiceModelDao.getId()) ||
committedReusedInvoiceId.contains(invoiceModelDao.getId());
if (InvoiceStatus.COMMITTED.equals(invoiceModelDao.getStatus())) {
-
if (wasInvoiceCreatedOrCommitted) {
notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, invoiceModelDao, context);
} else {
@@ -414,6 +413,9 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
notifyOfFutureBillingEvents(entitySqlDaoWrapperFactory, invoiceModelDao.getAccountId(), callbackDateTimePerSubscriptions, context);
}
+ // Bulk insert the invoice items
+ final List<InvoiceItemModelDao> createdInvoiceItems = createInvoiceItemsFromTransaction(transInvoiceItemSqlDao, invoiceItemsToCreate, context);
+
for (final UUID adjustedInvoiceId : allInvoiceIds) {
final boolean newInvoice = createdInvoiceIds.contains(adjustedInvoiceId);
if (newInvoice) {
@@ -431,7 +433,6 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
}
-
if (trackingIds != null && !trackingIds.isEmpty()) {
final InvoiceTrackingSqlDao trackingIdsSqlDao = entitySqlDaoWrapperFactory.become(InvoiceTrackingSqlDao.class);
trackingIdsSqlDao.create(trackingIds, context);
@@ -1133,15 +1134,25 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
}
- private InvoiceItemModelDao createInvoiceItemFromTransaction(final InvoiceItemSqlDao invoiceItemSqlDao, final InvoiceItemModelDao invoiceItemModelDao, final InternalCallContext context) throws EntityPersistenceException, InvoiceApiException {
- // There is no efficient way to retrieve an invoice item given an ID today (and invoice plugins can put item adjustments
- // on a different invoice than the original item), so it's easier to do the check in the DAO rather than in the API layer
- // See also https://github.com/killbill/killbill/issues/7
- if (InvoiceItemType.ITEM_ADJ.equals(invoiceItemModelDao.getType())) {
- validateInvoiceItemToBeAdjusted(invoiceItemSqlDao, invoiceItemModelDao, context);
+ private InvoiceItemModelDao createInvoiceItemFromTransaction(final InvoiceItemSqlDao invoiceItemSqlDao,
+ final InvoiceItemModelDao invoiceItemModelDao,
+ final InternalCallContext context) throws EntityPersistenceException, InvoiceApiException {
+ return Iterables.<InvoiceItemModelDao>getFirst(createInvoiceItemsFromTransaction(invoiceItemSqlDao, ImmutableList.<InvoiceItemModelDao>of(invoiceItemModelDao), context), null);
+ }
+
+ private List<InvoiceItemModelDao> createInvoiceItemsFromTransaction(final InvoiceItemSqlDao invoiceItemSqlDao,
+ final Iterable<InvoiceItemModelDao> invoiceItemModelDaos,
+ final InternalCallContext context) throws EntityPersistenceException, InvoiceApiException {
+ for (final InvoiceItemModelDao invoiceItemModelDao : invoiceItemModelDaos) {
+ // There is no efficient way to retrieve an invoice item given an ID today (and invoice plugins can put item adjustments
+ // on a different invoice than the original item), so it's easier to do the check in the DAO rather than in the API layer
+ // See also https://github.com/killbill/killbill/issues/7
+ if (InvoiceItemType.ITEM_ADJ.equals(invoiceItemModelDao.getType())) {
+ validateInvoiceItemToBeAdjusted(invoiceItemSqlDao, invoiceItemModelDao, context);
+ }
}
- return createAndRefresh(invoiceItemSqlDao, invoiceItemModelDao, context);
+ return createAndRefresh(invoiceItemSqlDao, invoiceItemModelDaos, context);
}
private void validateInvoiceItemToBeAdjusted(final InvoiceItemSqlDao invoiceItemSqlDao, final InvoiceItemModelDao invoiceItemModelDao, final InternalCallContext context) throws InvoiceApiException {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.java
index a9525e2..0059cac 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2018 Groupon, Inc
- * Copyright 2014-2018 The Billing Project, LLC
+ * Copyright 2014-2019 Groupon, Inc
+ * Copyright 2014-2019 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
@@ -29,7 +29,6 @@ import org.killbill.billing.util.entity.dao.EntitySqlDao;
import org.killbill.commons.jdbi.binder.SmartBindBean;
import org.killbill.commons.jdbi.template.KillBillSqlDaoStringTemplate;
import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.SqlBatch;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
import org.skife.jdbi.v2.sqlobject.SqlUpdate;
@@ -41,10 +40,6 @@ public interface InvoiceTrackingSqlDao extends EntitySqlDao<InvoiceTrackingModel
public void deactivateForInvoice(@Bind("invoiceId") String invoiceId,
@SmartBindBean final InternalCallContext context);
- @SqlBatch
- void create(@SmartBindBean Iterable<InvoiceTrackingModelDao> trackings,
- @SmartBindBean final InternalCallContext context);
-
@SqlQuery
List<InvoiceTrackingModelDao> getTrackingsByDateRange(@Bind("startDate") final Date startDate,
@Bind("endDate") final Date endDate,
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
index a36be45..c74a26d 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2018 Groupon, Inc
- * Copyright 2014-2018 The Billing Project, LLC
+ * Copyright 2014-2019 Groupon, Inc
+ * Copyright 2014-2019 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
@@ -46,6 +46,7 @@ import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase;
import org.killbill.billing.catalog.api.Product;
import org.killbill.billing.entity.EntityPersistenceException;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
import org.killbill.billing.invoice.InvoiceTestSuiteWithEmbeddedDB;
import org.killbill.billing.invoice.MockBillingEventSet;
import org.killbill.billing.invoice.api.Invoice;
@@ -73,7 +74,6 @@ import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
import org.killbill.billing.util.currency.KillBillMoney;
import org.killbill.clock.ClockMock;
import org.mockito.Mockito;
-import org.skife.jdbi.v2.exceptions.TransactionFailedException;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -117,6 +117,25 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
+ public void testSimpleInvoiceRun() throws Exception {
+ final UUID accountId = account.getId();
+
+ final InvoiceModelDao invoiceForExternalCharge = new InvoiceModelDao(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD, false);
+ final InvoiceItemModelDao externalCharge1 = new InvoiceItemModelDao(new ExternalChargeInvoiceItem(invoiceForExternalCharge.getId(), accountId, UUID.randomUUID(), UUID.randomUUID().toString(), clock.getUTCToday(), clock.getUTCToday(), new BigDecimal("15.0"), Currency.USD, null));
+ final InvoiceItemModelDao externalCharge2 = new InvoiceItemModelDao(new ExternalChargeInvoiceItem(invoiceForExternalCharge.getId(), accountId, UUID.randomUUID(), UUID.randomUUID().toString(), clock.getUTCToday(), clock.getUTCToday(), new BigDecimal("17.0"), Currency.USD, null));
+ invoiceForExternalCharge.addInvoiceItem(externalCharge1);
+ invoiceForExternalCharge.addInvoiceItem(externalCharge2);
+ invoiceDao.createInvoice(invoiceForExternalCharge,
+ ImmutableSet.<InvoiceTrackingModelDao>of(),
+ new FutureAccountNotifications(),
+ new ExistingInvoiceMetadata(ImmutableList.<Invoice>of()),
+ context);
+
+ final Invoice invoice = invoiceUserApi.getInvoice(invoiceForExternalCharge.getId(), callContext);
+ invoiceUtil.checkInvoicesEqual(invoiceForExternalCharge, invoice);
+ }
+
+ @Test(groups = "slow")
public void testCreationAndRetrievalByAccount() throws EntityPersistenceException {
final UUID accountId = account.getId();
final Invoice invoice = new DefaultInvoice(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
@@ -504,7 +523,6 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
assertEquals(invoices.size(), 0);
}
-
@Test(groups = "slow")
public void testAccountBalance() throws EntityPersistenceException {
final UUID accountId = account.getId();
@@ -564,7 +582,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID accountId = account.getId();
final UUID bundleId = UUID.randomUUID();
final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
- final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
+ final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
invoiceUtil.createInvoice(invoice1, context);
final LocalDate startDate = new LocalDate(2011, 3, 1);
@@ -823,7 +841,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final InvoiceModelDao invoiceForExternalCharge = new InvoiceModelDao(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD, false);
final InvoiceItemModelDao externalCharge = new InvoiceItemModelDao(new ExternalChargeInvoiceItem(invoiceForExternalCharge.getId(), accountId, bundleId, description, clock.getUTCToday(), clock.getUTCToday(), new BigDecimal("15.0"), Currency.USD, null));
invoiceForExternalCharge.addInvoiceItem(externalCharge);
- final InvoiceItemModelDao charge = invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(invoiceForExternalCharge), ImmutableSet.of(), context).get(0);
+ final InvoiceItemModelDao charge = invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(invoiceForExternalCharge), ImmutableSet.of(), context).get(0);
InvoiceModelDao newInvoice = invoiceDao.getById(charge.getInvoiceId(), context);
List<InvoiceItemModelDao> items = newInvoice.getInvoiceItems();
@@ -1593,7 +1611,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// * $5 item
// * $-5 CBA used
final DefaultInvoice invoice2 = new DefaultInvoice(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
- final InvoiceItem fixedItem2 = new FixedPriceInvoiceItem(invoice2.getId(), invoice1.getAccountId(), null, null, null, UUID.randomUUID().toString(),
+ final InvoiceItem fixedItem2 = new FixedPriceInvoiceItem(invoice2.getId(), invoice1.getAccountId(), null, null, null, UUID.randomUUID().toString(),
UUID.randomUUID().toString(), clock.getUTCToday(), new BigDecimal("5"), Currency.USD);
final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem2 = new CreditBalanceAdjInvoiceItem(fixedItem2.getInvoiceId(), fixedItem2.getAccountId(),
fixedItem2.getStartDate(), fixedItem2.getAmount().negate(),
@@ -1627,7 +1645,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// * $-10 repair
// * $10 generated CBA due to the repair (assume previous payment)
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
- final InvoiceItem fixedItem1 = new FixedPriceInvoiceItem(invoice1.getId(), invoice1.getAccountId(), null, null, null, UUID.randomUUID().toString(),
+ final InvoiceItem fixedItem1 = new FixedPriceInvoiceItem(invoice1.getId(), invoice1.getAccountId(), null, null, null, UUID.randomUUID().toString(),
UUID.randomUUID().toString(), clock.getUTCToday(), BigDecimal.TEN, Currency.USD);
final RepairAdjInvoiceItem repairAdjInvoiceItem = new RepairAdjInvoiceItem(fixedItem1.getInvoiceId(), fixedItem1.getAccountId(),
fixedItem1.getStartDate(), fixedItem1.getEndDate(),
@@ -1653,7 +1671,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// * $5 item
// * $-5 CBA used
final DefaultInvoice invoice2 = new DefaultInvoice(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
- final InvoiceItem fixedItem2 = new FixedPriceInvoiceItem(invoice2.getId(), invoice1.getAccountId(), null, null, null, UUID.randomUUID().toString(),
+ final InvoiceItem fixedItem2 = new FixedPriceInvoiceItem(invoice2.getId(), invoice1.getAccountId(), null, null, null, UUID.randomUUID().toString(),
UUID.randomUUID().toString(), clock.getUTCToday(), new BigDecimal("5"), Currency.USD);
final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem2 = new CreditBalanceAdjInvoiceItem(fixedItem2.getInvoiceId(), fixedItem2.getAccountId(),
fixedItem2.getStartDate(), fixedItem2.getAmount().negate(),
@@ -1667,7 +1685,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// * $5 item
// * $-5 CBA used
final DefaultInvoice invoice3 = new DefaultInvoice(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
- final InvoiceItem fixedItem3 = new FixedPriceInvoiceItem(invoice3.getId(), invoice1.getAccountId(), null, null, null, UUID.randomUUID().toString(),
+ final InvoiceItem fixedItem3 = new FixedPriceInvoiceItem(invoice3.getId(), invoice1.getAccountId(), null, null, null, UUID.randomUUID().toString(),
UUID.randomUUID().toString(), clock.getUTCToday(), new BigDecimal("5"), Currency.USD);
final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem3 = new CreditBalanceAdjInvoiceItem(fixedItem3.getInvoiceId(), fixedItem3.getAccountId(),
fixedItem3.getStartDate(), fixedItem3.getAmount().negate(),
@@ -1728,7 +1746,6 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
invoiceUtil.verifyInvoice(invoice1.getId(), 0.00, 10.00, context);
}
-
@Test(groups = "slow")
public void testWithFailedPaymentAttempt() throws Exception {
final UUID accountId = account.getId();
@@ -1737,14 +1754,13 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID bundleId = UUID.randomUUID();
final UUID subscriptionId = UUID.randomUUID();
- final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice.getId(), accountId, bundleId, subscriptionId, "test product", "test plan", "test ZOO", clock.getUTCNow().plusMonths(-1).toLocalDate(), clock.getUTCNow().toLocalDate(),
+ final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice.getId(), accountId, bundleId, subscriptionId, "test product", "test plan", "test ZOO", clock.getUTCNow().plusMonths(-1).toLocalDate(), clock.getUTCNow().toLocalDate(),
BigDecimal.TEN, BigDecimal.TEN, Currency.USD);
invoiceUtil.createInvoiceItem(item1, context);
final InvoiceModelDao retrievedInvoice = invoiceDao.getById(invoice.getId(), context);
assertEquals(retrievedInvoice.getInvoicePayments().size(), 0);
-
final UUID paymentId = UUID.randomUUID();
final DefaultInvoicePayment defaultInvoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoice.getId(), clock.getUTCNow().plusDays(12), BigDecimal.TEN, Currency.USD, Currency.USD, "cookie", false);
invoiceDao.notifyOfPaymentCompletion(new InvoicePaymentModelDao(defaultInvoicePayment), context);
@@ -1761,8 +1777,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
assertEquals(retrievedInvoice2.getInvoicePayments().get(0).getSuccess(), Boolean.TRUE);
}
-
- private InvoiceItemModelDao createCredit(final UUID accountId, final LocalDate effectiveDate, final BigDecimal creditAmount, final boolean draft)throws InvoiceApiException {
+ private InvoiceItemModelDao createCredit(final UUID accountId, final LocalDate effectiveDate, final BigDecimal creditAmount, final boolean draft) throws InvoiceApiException {
return createCredit(accountId, null, effectiveDate, creditAmount, draft);
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
index e61343d..202cbef 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2018 Groupon, Inc
- * Copyright 2014-2018 The Billing Project, LLC
+ * Copyright 2014-2019 Groupon, Inc
+ * Copyright 2014-2019 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
@@ -317,11 +317,7 @@ public class TestInvoiceHelper {
}
public void createPayment(final InvoicePayment invoicePayment, final InternalCallContext internalCallContext) {
- try {
- invoicePaymentSqlDao.create(new InvoicePaymentModelDao(invoicePayment), internalCallContext);
- } catch (final EntityPersistenceException e) {
- Assert.fail(e.getMessage());
- }
+ invoicePaymentSqlDao.create(new InvoicePaymentModelDao(invoicePayment), internalCallContext);
}
public void verifyInvoice(final UUID invoiceId, final double balance, final double cbaAmount, final InternalTenantContext context) throws InvoiceApiException {
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
index f1e5f39..806b386 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2018 Groupon, Inc
- * Copyright 2014-2018 The Billing Project, LLC
+ * Copyright 2014-2019 Groupon, Inc
+ * Copyright 2014-2019 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
@@ -35,7 +35,6 @@ import org.killbill.billing.catalog.api.PriceListSet;
import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.api.Entitlement;
import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
-import org.killbill.billing.entity.EntityPersistenceException;
import org.killbill.billing.subscription.SubscriptionTestSuiteWithEmbeddedDB;
import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
import org.killbill.billing.subscription.api.SubscriptionBillingApiException;
@@ -329,11 +328,7 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
final Handle handle = dbi.open();
final SubscriptionEventSqlDao sqlDao = handle.attach(SubscriptionEventSqlDao.class);
- try {
- sqlDao.create(newCancelEvent, internalCallContext);
- } catch (EntityPersistenceException e) {
- Assert.fail(e.getMessage());
- }
+ sqlDao.create(newCancelEvent, internalCallContext);
subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
// The extra cancel event is being ignored
diff --git a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.java b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.java
index 9e9ed9d..9624246 100644
--- a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.java
+++ b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2019 Groupon, Inc
+ * Copyright 2014-2019 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
@@ -22,23 +22,17 @@ import java.util.Date;
import java.util.List;
import java.util.UUID;
-import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.util.entity.Entity;
import org.killbill.billing.util.entity.dao.EntitySqlDao;
import org.killbill.commons.jdbi.binder.SmartBindBean;
import org.killbill.commons.jdbi.template.KillBillSqlDaoStringTemplate;
import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.SqlBatch;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
@KillBillSqlDaoStringTemplate
public interface RolledUpUsageSqlDao extends EntitySqlDao<RolledUpUsageModelDao, Entity> {
- @SqlBatch
- void create(@SmartBindBean Iterable<RolledUpUsageModelDao> usages,
- @SmartBindBean final InternalCallContext context);
-
@SqlQuery
Long recordsWithTrackingIdExist(@Bind("subscriptionId") final UUID subscriptionId,
@Bind("trackingId") final String trackingId,
diff --git a/util/src/main/java/org/killbill/billing/util/dao/AuditSqlDao.java b/util/src/main/java/org/killbill/billing/util/dao/AuditSqlDao.java
index e9cf5b8..9e06d81 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/AuditSqlDao.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/AuditSqlDao.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2019 Groupon, Inc
+ * Copyright 2014-2019 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
@@ -21,20 +21,20 @@ package org.killbill.billing.util.dao;
import java.util.Iterator;
import java.util.List;
-import org.killbill.commons.jdbi.template.KillBillSqlDaoStringTemplate;
-import org.skife.jdbi.v2.sqlobject.Bind;
-import org.killbill.commons.jdbi.binder.SmartBindBean;
-import org.skife.jdbi.v2.sqlobject.SqlQuery;
-import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-import org.skife.jdbi.v2.sqlobject.customizers.Define;
-
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.commons.jdbi.statement.SmartFetchSize;
import org.killbill.billing.util.audit.dao.AuditLogModelDao;
import org.killbill.billing.util.cache.Cachable;
import org.killbill.billing.util.cache.Cachable.CacheType;
import org.killbill.billing.util.cache.CachableKey;
+import org.killbill.commons.jdbi.binder.SmartBindBean;
+import org.killbill.commons.jdbi.statement.SmartFetchSize;
+import org.killbill.commons.jdbi.template.KillBillSqlDaoStringTemplate;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.customizers.BatchChunkSize;
+import org.skife.jdbi.v2.sqlobject.customizers.Define;
/**
* Note 1: cache invalidation has to happen for audit logs (which is tricky in the multi-nodes scenario).
@@ -49,9 +49,10 @@ import org.killbill.billing.util.cache.CachableKey;
// Note: @RegisterMapper annotation won't work here as we build the SqlObject via EntitySqlDao (annotations won't be inherited for JDBI)
public interface AuditSqlDao {
- @SqlUpdate
- public void insertAuditFromTransaction(@SmartBindBean final EntityAudit audit,
- @SmartBindBean final InternalCallContext context);
+ @SqlBatch
+ @BatchChunkSize(1000) // Arbitrary value, just a safety mechanism in case of very large datasets
+ public void insertAuditsFromTransaction(@SmartBindBean final Iterable<EntityAudit> audits,
+ @SmartBindBean final InternalCallContext context);
@SqlQuery
@SmartFetchSize(shouldStream = true)
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntityDaoBase.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntityDaoBase.java
index a4684b3..b92af82 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntityDaoBase.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntityDaoBase.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2019 Groupon, Inc
+ * Copyright 2014-2019 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
@@ -37,6 +37,7 @@ import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.Orderi
import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.PaginationIteratorBuilder;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
public abstract class EntityDaoBase<M extends EntityModelDao<E>, E extends Entity, U extends BillingExceptionBase> implements EntityDao<M, E, U> {
@@ -117,6 +118,15 @@ public abstract class EntityDaoBase<M extends EntityModelDao<E>, E extends Entit
return (F) transactional.create(entity, context);
}
+ protected <F extends EntityModelDao> List<F> createAndRefresh(final EntitySqlDao transactional, final Iterable<F> entities, final InternalCallContext context) throws EntityPersistenceException {
+ if (Iterables.<F>isEmpty(entities)) {
+ return ImmutableList.<F>of();
+ }
+
+ // We have overridden the jDBI return type in EntitySqlDaoWrapperInvocationHandler
+ return (List<F>) transactional.create(entities, context);
+ }
+
protected boolean checkEntityAlreadyExists(final EntitySqlDao<M, E> transactional, final M entity, final InternalCallContext context) {
return transactional.getRecordId(entity.getId().toString(), context) != null;
}
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDao.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDao.java
index a4ddb91..f61e7d5 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDao.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDao.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2011 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2019 Groupon, Inc
+ * Copyright 2014-2019 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
@@ -23,7 +23,6 @@ import java.util.List;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.billing.entity.EntityPersistenceException;
import org.killbill.billing.util.audit.ChangeType;
import org.killbill.billing.util.cache.Cachable;
import org.killbill.billing.util.cache.Cachable.CacheType;
@@ -31,12 +30,14 @@ import org.killbill.billing.util.cache.CachableKey;
import org.killbill.billing.util.dao.AuditSqlDao;
import org.killbill.billing.util.dao.HistorySqlDao;
import org.killbill.billing.util.entity.Entity;
+import org.killbill.commons.jdbi.binder.SmartBindBean;
import org.killbill.commons.jdbi.statement.SmartFetchSize;
import org.killbill.commons.jdbi.template.KillBillSqlDaoStringTemplate;
import org.skife.jdbi.v2.sqlobject.Bind;
-import org.killbill.commons.jdbi.binder.SmartBindBean;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.BatchChunkSize;
import org.skife.jdbi.v2.sqlobject.customizers.Define;
import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
@@ -47,7 +48,13 @@ public interface EntitySqlDao<M extends EntityModelDao<E>, E extends Entity> ext
@SqlUpdate
@Audited(ChangeType.INSERT)
public Object create(@SmartBindBean final M entity,
- @SmartBindBean final InternalCallContext context) throws EntityPersistenceException;
+ @SmartBindBean final InternalCallContext context);
+
+ @SqlBatch
+ @BatchChunkSize(1000) // Arbitrary value, just a safety mechanism in case of very large datasets
+ @Audited(ChangeType.INSERT)
+ public Object create(@SmartBindBean final Iterable<M> entity,
+ @SmartBindBean final InternalCallContext context);
@SqlQuery
public M getById(@Bind("id") final String id,
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
index 195d41d..c36bd94 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2012 Ning, Inc.
- * Copyright 2014-2018 Groupon, Inc
- * Copyright 2014-2018 The Billing Project, LLC
+ * Copyright 2014-2019 Groupon, Inc
+ * Copyright 2014-2019 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
@@ -31,6 +31,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -63,6 +64,7 @@ import org.skife.jdbi.v2.StatementContext;
import org.skife.jdbi.v2.exceptions.DBIException;
import org.skife.jdbi.v2.exceptions.StatementException;
import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
import org.skife.jdbi.v2.unstable.BindIn;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -335,19 +337,15 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>,
}
});
- M m = null;
- for (final String entityId : entityIds) {
- m = updateHistoryAndAudit(entityId, deletedEntities.get(entityId), changeType, context);
+ if (entityIds.isEmpty() ) {
+ return obj;
}
// PERF: override the return value with the reHydrated entity to avoid an extra 'get' in the transaction,
// (see EntityDaoBase#createAndRefresh for an example, but it works for updates as well).
- if (entityIds.size() == 1) {
- return m;
- } else {
- // jDBI will return the number of rows modified otherwise
- return obj;
- }
+ final List<M> ms = updateHistoryAndAudit(entityIds, deletedEntities, changeType, context);
+ final boolean isBatchQuery = method.getAnnotation(SqlBatch.class) != null;
+ return isBatchQuery ? ms : Iterables.<M>getFirst(ms, null);
}
private Object executeJDBCCall(final Method method, final Object[] args) throws IllegalAccessException, InvocationTargetException {
@@ -399,37 +397,52 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>,
rawKey;
}
- private M updateHistoryAndAudit(final String entityId, @Nullable final M deletedEntity, final ChangeType changeType, final InternalCallContext context) throws Throwable {
- final Object reHydratedEntity = prof.executeWithProfiling(ProfilingFeatureType.DAO_DETAILS, getProfilingId("history/audit", null), new WithProfilingCallback<Object, Throwable>() {
+ private List<M> updateHistoryAndAudit(final Collection<String> entityIds,
+ final Map<String, M> deletedEntities,
+ final ChangeType changeType,
+ final InternalCallContext context) throws Throwable {
+ final Object reHydratedEntities = prof.executeWithProfiling(ProfilingFeatureType.DAO_DETAILS, getProfilingId("history/audit", null), new WithProfilingCallback<Object, Throwable>() {
@Override
- public M execute() throws Throwable {
- final M reHydratedEntity;
- if (changeType == ChangeType.DELETE) {
- reHydratedEntity = deletedEntity;
- } else {
- // See note above regarding "markAsInactive" operations
- reHydratedEntity = MoreObjects.firstNonNull(sqlDao.getById(entityId, context), deletedEntity);
- printSQLWarnings();
- }
- Preconditions.checkNotNull(reHydratedEntity, "reHydratedEntity cannot be null");
- final Long entityRecordId = reHydratedEntity.getRecordId();
- final TableName tableName = reHydratedEntity.getTableName();
-
- // Note: audit entries point to the history record id
- final Long historyRecordId;
- if (tableName.getHistoryTableName() != null) {
- historyRecordId = insertHistory(entityRecordId, reHydratedEntity, changeType, context);
- } else {
- historyRecordId = entityRecordId;
+ public List<M> execute() {
+ TableName tableName = null;
+ // We'll keep the ordering
+ final Map<M, Long> reHydratedEntityModelDaoAndHistoryRecordIds = new LinkedHashMap<M, Long>(entityIds.size());
+ for (final String entityId : entityIds) {
+ final M reHydratedEntity;
+ if (changeType == ChangeType.DELETE) {
+ reHydratedEntity = deletedEntities.get(entityId);
+ } else {
+ // See note above regarding "markAsInactive" operations
+ // TODO Could we avoid this query?
+ reHydratedEntity = MoreObjects.firstNonNull(sqlDao.getById(entityId, context), deletedEntities.get(entityId));
+ printSQLWarnings();
+ }
+ Preconditions.checkNotNull(reHydratedEntity, "reHydratedEntity cannot be null");
+ final Long entityRecordId = reHydratedEntity.getRecordId();
+ if (tableName == null) {
+ tableName = reHydratedEntity.getTableName();
+ }
+
+ // Note: audit entries point to the history record id
+ final Long historyRecordId;
+ if (tableName.getHistoryTableName() != null) {
+ // TODO Could we do this in bulk too?
+ historyRecordId = insertHistory(entityRecordId, reHydratedEntity, changeType, context);
+ } else {
+ historyRecordId = entityRecordId;
+ }
+
+ reHydratedEntityModelDaoAndHistoryRecordIds.put(reHydratedEntity, historyRecordId);
}
- // Make sure to re-hydrate the object (especially needed for create calls)
- insertAudits(tableName, reHydratedEntity, entityRecordId, historyRecordId, changeType, context);
- return reHydratedEntity;
+ // Make sure to re-hydrate the objects first (especially needed for create calls)
+ insertAudits(tableName, reHydratedEntityModelDaoAndHistoryRecordIds, changeType, context);
+
+ return ImmutableList.<M>copyOf(reHydratedEntityModelDaoAndHistoryRecordIds.keySet());
}
});
//noinspection unchecked
- return (M) reHydratedEntity;
+ return (List<M>) reHydratedEntities;
}
private List<String> retrieveEntityIdsFromArguments(final Method method, final Object[] args) {
@@ -510,35 +523,51 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>,
return recordId;
}
- private void insertAudits(final TableName tableName, final M entityModelDao, final Long entityRecordId, final Long historyRecordId, final ChangeType changeType, final InternalCallContext contextMaybeWithoutAccountRecordId) {
+ private void insertAudits(final TableName tableName,
+ final Map<M, Long> entityModelDaoAndHistoryRecordIds,
+ final ChangeType changeType,
+ final InternalCallContext contextMaybeWithoutAccountRecordId) {
final TableName destinationTableName = MoreObjects.firstNonNull(tableName.getHistoryTableName(), tableName);
- final EntityAudit audit = new EntityAudit(destinationTableName, historyRecordId, changeType, contextMaybeWithoutAccountRecordId.getCreatedDate());
-
- final InternalCallContext context;
- // Populate the account record id when creating the account record
- if (TableName.ACCOUNT.equals(tableName) && ChangeType.INSERT.equals(changeType)) {
- // AccountModelDao in practice
- final TimeZoneAwareEntity accountModelDao = (TimeZoneAwareEntity) entityModelDao;
- context = internalCallContextFactory.createInternalCallContext(accountModelDao, entityRecordId, contextMaybeWithoutAccountRecordId);
- } else {
- context = contextMaybeWithoutAccountRecordId;
+
+ InternalCallContext context = null;
+ final Collection<EntityAudit> audits = new LinkedList<EntityAudit>();
+ for (final M entityModelDao : entityModelDaoAndHistoryRecordIds.keySet()) {
+ final Long targetRecordId = entityModelDaoAndHistoryRecordIds.get(entityModelDao);
+ final EntityAudit audit = new EntityAudit(destinationTableName, targetRecordId, changeType, contextMaybeWithoutAccountRecordId.getCreatedDate());
+ audits.add(audit);
+
+ if (context == null) {
+ // Populate the account record id when creating the account record
+ if (TableName.ACCOUNT.equals(tableName) && ChangeType.INSERT.equals(changeType)) {
+ // AccountModelDao in practice
+ final TimeZoneAwareEntity accountModelDao = (TimeZoneAwareEntity) entityModelDao;
+ context = internalCallContextFactory.createInternalCallContext(accountModelDao, entityModelDao.getRecordId(), contextMaybeWithoutAccountRecordId);
+ } else {
+ context = contextMaybeWithoutAccountRecordId;
+ }
+ }
}
- sqlDao.insertAuditFromTransaction(audit, context);
+
+ sqlDao.insertAuditsFromTransaction(audits, context);
printSQLWarnings();
// We need to invalidate the caches. There is a small window of doom here where caches will be stale.
- // TODO Knowledge on how the key is constructed is also in AuditSqlDao
- if (tableName.getHistoryTableName() != null) {
- final CacheController<String, List> cacheController = cacheControllerDispatcher.getCacheController(CacheType.AUDIT_LOG_VIA_HISTORY);
- if (cacheController != null) {
- final String key = buildCacheKey(ImmutableMap.<Integer, Object>of(0, tableName.getHistoryTableName(), 1, tableName.getHistoryTableName(), 2, entityRecordId));
- cacheController.remove(key);
- }
- } else {
- final CacheController<String, List> cacheController = cacheControllerDispatcher.getCacheController(CacheType.AUDIT_LOG);
- if (cacheController != null) {
- final String key = buildCacheKey(ImmutableMap.<Integer, Object>of(0, tableName, 1, entityRecordId));
- cacheController.remove(key);
+ for (final M entityModelDao : entityModelDaoAndHistoryRecordIds.keySet()) {
+ final Long entityRecordId = entityModelDao.getRecordId();
+
+ // TODO Knowledge on how the key is constructed is also in AuditSqlDao
+ if (tableName.getHistoryTableName() != null) {
+ final CacheController<String, List> cacheController = cacheControllerDispatcher.getCacheController(CacheType.AUDIT_LOG_VIA_HISTORY);
+ if (cacheController != null) {
+ final String key = buildCacheKey(ImmutableMap.<Integer, Object>of(0, tableName.getHistoryTableName(), 1, tableName.getHistoryTableName(), 2, entityRecordId));
+ cacheController.remove(key);
+ }
+ } else {
+ final CacheController<String, List> cacheController = cacheControllerDispatcher.getCacheController(CacheType.AUDIT_LOG);
+ if (cacheController != null) {
+ final String key = buildCacheKey(ImmutableMap.<Integer, Object>of(0, tableName, 1, entityRecordId));
+ cacheController.remove(key);
+ }
}
}
}
diff --git a/util/src/main/resources/org/killbill/billing/util/entity/dao/EntitySqlDao.sql.stg b/util/src/main/resources/org/killbill/billing/util/entity/dao/EntitySqlDao.sql.stg
index 5b861e8..ab7f88f 100644
--- a/util/src/main/resources/org/killbill/billing/util/entity/dao/EntitySqlDao.sql.stg
+++ b/util/src/main/resources/org/killbill/billing/util/entity/dao/EntitySqlDao.sql.stg
@@ -364,7 +364,7 @@ values (
>>
-insertAuditFromTransaction() ::= <<
+insertAuditsFromTransaction() ::= <<
insert into <auditTableName()> (
<auditTableFields("")>
)
diff --git a/util/src/test/java/org/killbill/billing/util/dao/TestStringTemplateInheritance.java b/util/src/test/java/org/killbill/billing/util/dao/TestStringTemplateInheritance.java
index c099f56..56caa8d 100644
--- a/util/src/test/java/org/killbill/billing/util/dao/TestStringTemplateInheritance.java
+++ b/util/src/test/java/org/killbill/billing/util/dao/TestStringTemplateInheritance.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2012 Ning, Inc.
+ * Copyright 2014-2019 Groupon, Inc
+ * Copyright 2014-2019 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -139,33 +141,33 @@ public class TestStringTemplateInheritance extends UtilTestSuiteNoDB {
"\\)\r?\n" +
";");
- assertPattern(kombucha.getInstanceOf("insertAuditFromTransaction").render(), "insert into audit_log \\(\r?\n" +
- "id\r?\n" +
- ", table_name\r?\n" +
- ", target_record_id\r?\n" +
- ", change_type\r?\n" +
- ", created_by\r?\n" +
- ", reason_code\r?\n" +
- ", comments\r?\n" +
- ", user_token\r?\n" +
- ", created_date\r?\n" +
- ", account_record_id\r?\n" +
- ", tenant_record_id\r?\n" +
- "\\)\r?\n" +
- "values \\(\r?\n" +
- " :id\r?\n" +
- ", :tableName\r?\n" +
- ", :targetRecordId\r?\n" +
- ", :changeType\r?\n" +
- ", :createdBy\r?\n" +
- ", :reasonCode\r?\n" +
- ", :comments\r?\n" +
- ", :userToken\r?\n" +
- ", :createdDate\r?\n" +
- ", :accountRecordId\r?\n" +
- ", :tenantRecordId\r?\n" +
- "\\)\r?\n" +
- ";");
+ assertPattern(kombucha.getInstanceOf("insertAuditsFromTransaction").render(), "insert into audit_log \\(\r?\n" +
+ "id\r?\n" +
+ ", table_name\r?\n" +
+ ", target_record_id\r?\n" +
+ ", change_type\r?\n" +
+ ", created_by\r?\n" +
+ ", reason_code\r?\n" +
+ ", comments\r?\n" +
+ ", user_token\r?\n" +
+ ", created_date\r?\n" +
+ ", account_record_id\r?\n" +
+ ", tenant_record_id\r?\n" +
+ "\\)\r?\n" +
+ "values \\(\r?\n" +
+ " :id\r?\n" +
+ ", :tableName\r?\n" +
+ ", :targetRecordId\r?\n" +
+ ", :changeType\r?\n" +
+ ", :createdBy\r?\n" +
+ ", :reasonCode\r?\n" +
+ ", :comments\r?\n" +
+ ", :userToken\r?\n" +
+ ", :createdDate\r?\n" +
+ ", :accountRecordId\r?\n" +
+ ", :tenantRecordId\r?\n" +
+ "\\)\r?\n" +
+ ";");
}
private void assertPattern(final String actual, final String expected) {