killbill-aplcache
Changes
subscription/src/main/java/org/killbill/billing/subscription/engine/dao/BundleSqlDao.java 12(+10 -2)
subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java 22(+18 -4)
subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/BundleSqlDao.sql.stg 9(+9 -0)
Details
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/BundleSqlDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/BundleSqlDao.java
index 587d390..8d5de31 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/BundleSqlDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/BundleSqlDao.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 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:
*
@@ -16,6 +18,7 @@
package org.killbill.billing.subscription.engine.dao;
+import java.util.Collection;
import java.util.Date;
import java.util.List;
@@ -32,6 +35,7 @@ import org.skife.jdbi.v2.sqlobject.Bind;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
import org.skife.jdbi.v2.sqlobject.SqlUpdate;
import org.skife.jdbi.v2.sqlobject.customizers.Define;
+import org.skife.jdbi.v2.unstable.BindIn;
@KillBillSqlDaoStringTemplate
public interface BundleSqlDao extends EntitySqlDao<SubscriptionBundleModelDao, SubscriptionBaseBundle> {
@@ -44,7 +48,7 @@ public interface BundleSqlDao extends EntitySqlDao<SubscriptionBundleModelDao, S
@SqlUpdate
@Audited(ChangeType.UPDATE)
- public void renameBundleExternalKey(@Bind("externalKey") String externalKey,
+ public void renameBundleExternalKey(@BindIn("ids") final Collection<String> ids,
@Define("prefix") final String prefix,
@SmartBindBean final InternalCallContext context);
@@ -64,6 +68,10 @@ public interface BundleSqlDao extends EntitySqlDao<SubscriptionBundleModelDao, S
@SmartBindBean final InternalTenantContext context);
@SqlQuery
+ public List<SubscriptionBundleModelDao> getBundlesForKey(@Bind("externalKey") String externalKey,
+ @SmartBindBean final InternalTenantContext context);
+
+ @SqlQuery
public List<SubscriptionBundleModelDao> getBundlesForLikeKey(@Bind("externalKey") String externalKey,
@SmartBindBean final InternalTenantContext context);
}
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 31d1c4d..fe31f97 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -323,9 +323,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS, bundle.getExternalKey());
} else if (renameCancelledBundleIfExist) {
log.info("Renaming bundles with externalKey='{}', prefix='cncl'", bundle.getExternalKey());
- // Note that if bundle belongs to a different account, context is not the context for this target account,
- // but the underlying sql operation does not use the account info
- bundleSqlDao.renameBundleExternalKey(bundle.getExternalKey(), "cncl", context);
+ renameBundleExternalKey(bundleSqlDao, bundle.getExternalKey(), "cncl", context);
} /* else {
Code will throw SQLIntegrityConstraintViolationException because of unique constraint on externalKey; might be worth having an ErrorCode just for that
} */
@@ -346,6 +344,22 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
});
}
+ // Note that if bundle belongs to a different account, context is not the context for this target account,
+ // but the underlying sql operation does not use the account info
+ private void renameBundleExternalKey(final BundleSqlDao bundleSqlDao, final String externalKey, final String prefix, final InternalCallContext context) {
+ final List<SubscriptionBundleModelDao> bundleModelDaos = bundleSqlDao.getBundlesForKey(externalKey, context);
+ if (!bundleModelDaos.isEmpty()) {
+ final Collection<String> bundleIdsToRename = Collections2.<SubscriptionBundleModelDao, String>transform(bundleModelDaos,
+ new Function<SubscriptionBundleModelDao, String>() {
+ @Override
+ public String apply(final SubscriptionBundleModelDao input) {
+ return input.getId().toString();
+ }
+ });
+ bundleSqlDao.renameBundleExternalKey(bundleIdsToRename, prefix, context);
+ }
+ }
+
@Override
public SubscriptionBase getBaseSubscription(final UUID bundleId, final Catalog catalog, final InternalTenantContext context) throws CatalogApiException {
return getBaseSubscription(bundleId, true, catalog, context);
@@ -1012,7 +1026,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
// Rename externalKey from source bundle
final BundleSqlDao bundleSqlDao = entitySqlDaoWrapperFactory.become(BundleSqlDao.class);
- bundleSqlDao.renameBundleExternalKey(bundleTransferData.getData().getExternalKey(), "tsf", fromContext);
+ renameBundleExternalKey(bundleSqlDao, bundleTransferData.getData().getExternalKey(), "tsf", fromContext);
final SubscriptionEventSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class);
transferBundleDataFromTransaction(bundleTransferData, transactional, entitySqlDaoWrapperFactory, toContext);
diff --git a/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/BundleSqlDao.sql.stg b/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/BundleSqlDao.sql.stg
index ecfeb0f..886d776 100644
--- a/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/BundleSqlDao.sql.stg
+++ b/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/BundleSqlDao.sql.stg
@@ -51,8 +51,17 @@ where id = :id
renameBundleExternalKey(prefix) ::= <<
update bundles
set external_key = concat('kb', '<prefix>', '-', record_id, ':', external_key)
+where <idField("")> in (<ids>)
+<AND_CHECK_TENANT("")>
+;
+>>
+
+getBundlesForKey() ::= <<
+select <allTableFields("")>
+from bundles
where external_key = :externalKey
<AND_CHECK_TENANT("")>
+<defaultOrderBy("")>
;
>>
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/TestSubscriptionDao.java b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/TestSubscriptionDao.java
index 7ca3747..4e0eef5 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/TestSubscriptionDao.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/TestSubscriptionDao.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
@@ -22,6 +22,7 @@ import java.util.UUID;
import org.joda.time.DateTime;
import org.killbill.billing.ErrorCode;
+import org.killbill.billing.ObjectType;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountData;
import org.killbill.billing.api.TestApiListener.NextEvent;
@@ -40,7 +41,14 @@ import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
import org.killbill.billing.subscription.events.user.ApiEventBuilder;
import org.killbill.billing.subscription.events.user.ApiEventCreate;
import org.killbill.billing.util.UUIDs;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.ChangeType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.entity.dao.DBRouterUntyped;
+import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
import org.killbill.commons.profiling.Profiling.WithProfilingCallback;
import org.mockito.Mockito;
import org.skife.jdbi.v2.IDBI;
@@ -56,6 +64,7 @@ import static org.testng.Assert.assertEquals;
public class TestSubscriptionDao extends SubscriptionTestSuiteWithEmbeddedDB {
+ private EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
protected UUID accountId;
@Override
@@ -72,6 +81,8 @@ public class TestSubscriptionDao extends SubscriptionTestSuiteWithEmbeddedDB {
final AccountData accountData = subscriptionTestInitializer.initAccountData(clock);
final Account account = createAccount(accountData);
accountId = account.getId();
+
+ transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi, roDbi, clock, new CacheControllerDispatcher(), nonEntityDao, internalCallContextFactory);
}
@Override // to ignore events
@@ -154,26 +165,55 @@ public class TestSubscriptionDao extends SubscriptionTestSuiteWithEmbeddedDB {
assertEquals(result.size(), 1);
assertEquals(result.get(0).getExternalKey(), bundle.getExternalKey());
+ final List<AuditLog> auditLogsBeforeRenaming = auditUserApi.getAuditLogs(bundle.getId(), ObjectType.BUNDLE, AuditLevel.FULL, callContext);
+ assertEquals(auditLogsBeforeRenaming.size(), 1);
+ assertEquals(auditLogsBeforeRenaming.get(0).getChangeType(), ChangeType.INSERT);
+
// Update key to 'internal KB value 'kbtsf-12345:'
dao.updateBundleExternalKey(bundle.getId(), "kbtsf-12345:" + bundle.getExternalKey(), internalCallContext);
final List<SubscriptionBaseBundle> result2 = dao.getSubscriptionBundlesForKey(externalKey, internalCallContext);
assertEquals(result2.size(), 1);
+ final List<AuditLog> auditLogsAfterRenaming = auditUserApi.getAuditLogs(bundle.getId(), ObjectType.BUNDLE, AuditLevel.FULL, callContext);
+ assertEquals(auditLogsAfterRenaming.size(), 2);
+ assertEquals(auditLogsAfterRenaming.get(0).getChangeType(), ChangeType.INSERT);
+ assertEquals(auditLogsAfterRenaming.get(1).getChangeType(), ChangeType.UPDATE);
+
// Create new bundle with original key, verify all results show original key, stripping down internal prefix
final DefaultSubscriptionBaseBundle bundleDef2 = new DefaultSubscriptionBaseBundle(externalKey, accountId, startDate, startDate, createdDate, createdDate);
final SubscriptionBaseBundle bundle2 = dao.createSubscriptionBundle(bundleDef2, catalog, true, internalCallContext);
final List<SubscriptionBaseBundle> result3 = dao.getSubscriptionBundlesForKey(externalKey, internalCallContext);
assertEquals(result3.size(), 2);
+ assertEquals(result3.get(0).getId(), bundle.getId());
assertEquals(result3.get(0).getExternalKey(), bundle2.getExternalKey());
+ assertEquals(result3.get(1).getId(), bundle2.getId());
assertEquals(result3.get(1).getExternalKey(), bundle2.getExternalKey());
+ final List<AuditLog> auditLogs2BeforeRenaming = auditUserApi.getAuditLogs(bundle2.getId(), ObjectType.BUNDLE, AuditLevel.FULL, callContext);
+ assertEquals(auditLogs2BeforeRenaming.size(), 1);
+ assertEquals(auditLogs2BeforeRenaming.get(0).getChangeType(), ChangeType.INSERT);
+
// This time we call the lower SqlDao to rename the bundle automatically and verify we still get same # results,
// with original key
- dbi.onDemand(BundleSqlDao.class).renameBundleExternalKey(externalKey, "foo", internalCallContext);
+ transactionalSqlDao.execute(false,
+ new EntitySqlDaoTransactionWrapper<Void>() {
+ @Override
+ public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+ entitySqlDaoWrapperFactory.become(BundleSqlDao.class).renameBundleExternalKey(ImmutableList.<String>of(bundle2.getId().toString()), "foo", internalCallContext);
+ return null;
+ }
+ });
final List<SubscriptionBaseBundle> result4 = dao.getSubscriptionBundlesForKey(externalKey, internalCallContext);
assertEquals(result4.size(), 2);
assertEquals(result4.get(0).getExternalKey(), bundle2.getExternalKey());
+ assertEquals(result4.get(0).getId(), bundle.getId());
assertEquals(result4.get(1).getExternalKey(), bundle2.getExternalKey());
+ assertEquals(result4.get(1).getId(), bundle2.getId());
+
+ final List<AuditLog> auditLogs2AfterRenaming = auditUserApi.getAuditLogs(bundle2.getId(), ObjectType.BUNDLE, AuditLevel.FULL, callContext);
+ assertEquals(auditLogs2AfterRenaming.size(), 2);
+ assertEquals(auditLogs2AfterRenaming.get(0).getChangeType(), ChangeType.INSERT);
+ assertEquals(auditLogs2AfterRenaming.get(1).getChangeType(), ChangeType.UPDATE);
// Create bundle one more time
final DefaultSubscriptionBaseBundle bundleDef3 = new DefaultSubscriptionBaseBundle(externalKey, accountId, startDate, startDate, createdDate, createdDate);
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
index 5a59f67..260c0da 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.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
@@ -41,6 +41,7 @@ import org.killbill.billing.subscription.api.user.TestSubscriptionHelper;
import org.killbill.billing.subscription.engine.addon.AddonUtils;
import org.killbill.billing.subscription.engine.dao.SubscriptionDao;
import org.killbill.billing.subscription.glue.TestDefaultSubscriptionModuleWithEmbeddedDB;
+import org.killbill.billing.util.api.AuditUserApi;
import org.killbill.billing.util.config.definition.SubscriptionConfig;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.bus.api.PersistentBus;
@@ -91,6 +92,8 @@ public class SubscriptionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteW
protected TestApiListener testListener;
@Inject
protected SubscriptionTestInitializer subscriptionTestInitializer;
+ @Inject
+ protected AuditUserApi auditUserApi;
@Inject
protected NonEntityDao nonEntityDao;