Details
diff --git a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
index 798d720..46a3ea0 100644
--- a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
@@ -36,6 +36,8 @@ import org.killbill.billing.account.dao.AccountEmailModelDao;
import org.killbill.billing.account.dao.AccountModelDao;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
@@ -232,4 +234,9 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
}
}));
}
+
+ @Override
+ public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final UUID accountId, final AuditLevel auditLevel, final TenantContext tenantContext) throws AccountApiException {
+ return accountDao.getAuditLogsWithHistoryForId(accountId, auditLevel, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
+ }
}
diff --git a/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java b/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java
index 1446204..1e0e94c 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java
@@ -23,6 +23,8 @@ import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.EntityDao;
@@ -54,4 +56,6 @@ public interface AccountDao extends EntityDao<AccountModelDao, Account, AccountA
Integer getAccountBCD(UUID accountId, InternalTenantContext context);
List<AccountModelDao> getAccountsByParentId(UUID parentAccountId, InternalTenantContext context);
+
+ List<AuditLogWithHistory> getAuditLogsWithHistoryForId(UUID accountId, AuditLevel auditLevel, InternalTenantContext context) throws AccountApiException;
}
diff --git a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
index a2a5ea3..d0c412c 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
@@ -38,12 +38,16 @@ import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.entity.EntityPersistenceException;
import org.killbill.billing.events.AccountChangeInternalEvent;
import org.killbill.billing.events.AccountCreationInternalEvent;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.audit.ChangeType;
+import org.killbill.billing.util.audit.dao.AuditDao;
import org.killbill.billing.util.cache.Cachable.CacheType;
import org.killbill.billing.util.cache.CacheController;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.billing.util.dao.TableName;
import org.killbill.billing.util.entity.DefaultPagination;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.Ordering;
@@ -72,15 +76,17 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
private final PersistentBus eventBus;
private final InternalCallContextFactory internalCallContextFactory;
private final Clock clock;
+ private final AuditDao auditDao;
@Inject
public DefaultAccountDao(final IDBI dbi, @Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi, final PersistentBus eventBus, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
- final InternalCallContextFactory internalCallContextFactory, final NonEntityDao nonEntityDao) {
+ final InternalCallContextFactory internalCallContextFactory, final NonEntityDao nonEntityDao, final AuditDao auditDao) {
super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, roDbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory), AccountSqlDao.class);
this.accountImmutableCacheController = cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_IMMUTABLE);
this.eventBus = eventBus;
this.internalCallContextFactory = internalCallContextFactory;
this.clock = clock;
+ this.auditDao = auditDao;
}
@Override
@@ -317,4 +323,15 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
}
});
}
+
+ @Override
+ public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final UUID accountId, final AuditLevel auditLevel, final InternalTenantContext context) throws AccountApiException {
+ return transactionalSqlDao.execute(true, AccountApiException.class, new EntitySqlDaoTransactionWrapper<List<AuditLogWithHistory>>() {
+ @Override
+ public List<AuditLogWithHistory> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) {
+ final AccountSqlDao transactional = entitySqlDaoWrapperFactory.become(AccountSqlDao.class);
+ return auditDao.getAuditLogsWithHistoryForId(transactional, TableName.ACCOUNT, accountId, auditLevel, context);
+ }
+ });
+ }
}
diff --git a/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java b/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
index 9d097c4..077ca8d 100644
--- a/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
@@ -37,6 +37,8 @@ import org.killbill.billing.account.api.user.DefaultAccountCreationEvent.Default
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.events.AccountChangeInternalEvent;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.entity.DefaultPagination;
import org.killbill.billing.util.entity.Pagination;
@@ -186,4 +188,9 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
}
}));
}
+
+ @Override
+ public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final UUID accountId, final AuditLevel auditLevel, final InternalTenantContext context) throws AccountApiException {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java b/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java
index 3db923f..8a97b2a 100644
--- a/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java
+++ b/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java
@@ -26,7 +26,6 @@ import org.joda.time.DateTimeZone;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.ObjectType;
import org.killbill.billing.account.AccountTestSuiteWithEmbeddedDB;
-import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.account.api.AccountData;
import org.killbill.billing.account.api.AccountEmail;
@@ -40,10 +39,10 @@ import org.killbill.billing.util.api.CustomFieldApiException;
import org.killbill.billing.util.api.TagApiException;
import org.killbill.billing.util.api.TagDefinitionApiException;
import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.audit.ChangeType;
import org.killbill.billing.util.audit.DefaultAccountAuditLogs;
import org.killbill.billing.util.customfield.dao.CustomFieldModelDao;
-import org.killbill.billing.util.dao.EntityHistoryModelDao;
import org.killbill.billing.util.dao.TableName;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.tag.DescriptiveTag;
@@ -214,17 +213,18 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
final AccountModelDao account = createTestAccount();
accountDao.create(account, internalCallContext);
final AccountModelDao createdAccount = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
-
- final List<EntityHistoryModelDao<AccountModelDao, Account>> history1 = getAccountHistory(createdAccount.getRecordId());
- Assert.assertEquals(history1.size(), 1);
- Assert.assertEquals(history1.get(0).getChangeType(), ChangeType.INSERT);
- Assert.assertEquals(history1.get(0).getEntity().getAccountRecordId(), createdAccount.getRecordId());
- Assert.assertEquals(history1.get(0).getEntity().getTenantRecordId(), createdAccount.getTenantRecordId());
- Assert.assertEquals(history1.get(0).getEntity().getExternalKey(), createdAccount.getExternalKey());
- Assert.assertEquals(history1.get(0).getEntity().getMigrated(), createdAccount.getMigrated());
- Assert.assertEquals(history1.get(0).getEntity().getIsNotifiedForInvoices(), createdAccount.getIsNotifiedForInvoices());
- Assert.assertEquals(history1.get(0).getEntity().getTimeZone(), createdAccount.getTimeZone());
- Assert.assertEquals(history1.get(0).getEntity().getLocale(), createdAccount.getLocale());
+ List<AuditLogWithHistory> auditLogsWithHistory = accountDao.getAuditLogsWithHistoryForId(account.getId(), AuditLevel.FULL, internalCallContext);
+ Assert.assertEquals(auditLogsWithHistory.size(), 1);
+
+ final AccountModelDao history1 = (AccountModelDao) auditLogsWithHistory.get(0).getEntity();
+ Assert.assertEquals(auditLogsWithHistory.get(0).getChangeType(), ChangeType.INSERT);
+ Assert.assertEquals(history1.getAccountRecordId(), createdAccount.getRecordId());
+ Assert.assertEquals(history1.getTenantRecordId(), createdAccount.getTenantRecordId());
+ Assert.assertEquals(history1.getExternalKey(), createdAccount.getExternalKey());
+ Assert.assertEquals(history1.getMigrated(), createdAccount.getMigrated());
+ Assert.assertEquals(history1.getIsNotifiedForInvoices(), createdAccount.getIsNotifiedForInvoices());
+ Assert.assertEquals(history1.getTimeZone(), createdAccount.getTimeZone());
+ Assert.assertEquals(history1.getLocale(), createdAccount.getLocale());
final AccountData accountData = new MockAccountBuilder(new DefaultAccount(account)).migrated(false)
.isNotifiedForInvoices(false)
@@ -236,18 +236,19 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
final AccountModelDao retrievedAccount = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
checkAccountsEqual(retrievedAccount, updatedAccount);
-
- final List<EntityHistoryModelDao<AccountModelDao, Account>> history2 = getAccountHistory(createdAccount.getRecordId());
- Assert.assertEquals(history2.size(), 2);
- Assert.assertEquals(history2.get(0).getChangeType(), ChangeType.INSERT);
- Assert.assertEquals(history2.get(1).getChangeType(), ChangeType.UPDATE);
- Assert.assertEquals(history2.get(1).getEntity().getAccountRecordId(), retrievedAccount.getRecordId());
- Assert.assertEquals(history2.get(1).getEntity().getTenantRecordId(), retrievedAccount.getTenantRecordId());
- Assert.assertEquals(history2.get(1).getEntity().getExternalKey(), retrievedAccount.getExternalKey());
- Assert.assertEquals(history2.get(1).getEntity().getMigrated(), retrievedAccount.getMigrated());
- Assert.assertEquals(history2.get(1).getEntity().getIsNotifiedForInvoices(), retrievedAccount.getIsNotifiedForInvoices());
- Assert.assertEquals(history2.get(1).getEntity().getTimeZone(), retrievedAccount.getTimeZone());
- Assert.assertEquals(history2.get(1).getEntity().getLocale(), retrievedAccount.getLocale());
+ auditLogsWithHistory = accountDao.getAuditLogsWithHistoryForId(retrievedAccount.getId(), AuditLevel.FULL, internalCallContext);
+ Assert.assertEquals(auditLogsWithHistory.size(), 2);
+
+ final AccountModelDao history2 = (AccountModelDao) auditLogsWithHistory.get(1).getEntity();
+ Assert.assertEquals(auditLogsWithHistory.get(0).getChangeType(), ChangeType.INSERT);
+ Assert.assertEquals(auditLogsWithHistory.get(1).getChangeType(), ChangeType.UPDATE);
+ Assert.assertEquals(history2.getAccountRecordId(), retrievedAccount.getRecordId());
+ Assert.assertEquals(history2.getTenantRecordId(), retrievedAccount.getTenantRecordId());
+ Assert.assertEquals(history2.getExternalKey(), retrievedAccount.getExternalKey());
+ Assert.assertEquals(history2.getMigrated(), retrievedAccount.getMigrated());
+ Assert.assertEquals(history2.getIsNotifiedForInvoices(), retrievedAccount.getIsNotifiedForInvoices());
+ Assert.assertEquals(history2.getTimeZone(), retrievedAccount.getTimeZone());
+ Assert.assertEquals(history2.getLocale(), retrievedAccount.getLocale());
final AccountData accountData2 = new MockAccountBuilder(new DefaultAccount(updatedAccount)).isNotifiedForInvoices(true)
.locale("en_US")
@@ -257,19 +258,20 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
final AccountModelDao retrievedAccount2 = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
checkAccountsEqual(retrievedAccount2, updatedAccount2);
-
- final List<EntityHistoryModelDao<AccountModelDao, Account>> history3 = getAccountHistory(createdAccount.getRecordId());
- Assert.assertEquals(history3.size(), 3);
- Assert.assertEquals(history3.get(0).getChangeType(), ChangeType.INSERT);
- Assert.assertEquals(history3.get(1).getChangeType(), ChangeType.UPDATE);
- Assert.assertEquals(history3.get(2).getChangeType(), ChangeType.UPDATE);
- Assert.assertEquals(history3.get(2).getEntity().getAccountRecordId(), retrievedAccount2.getRecordId());
- Assert.assertEquals(history3.get(2).getEntity().getTenantRecordId(), retrievedAccount2.getTenantRecordId());
- Assert.assertEquals(history3.get(2).getEntity().getExternalKey(), retrievedAccount2.getExternalKey());
- Assert.assertEquals(history3.get(2).getEntity().getMigrated(), retrievedAccount2.getMigrated());
- Assert.assertEquals(history3.get(2).getEntity().getIsNotifiedForInvoices(), retrievedAccount2.getIsNotifiedForInvoices());
- Assert.assertEquals(history3.get(2).getEntity().getTimeZone(), retrievedAccount2.getTimeZone());
- Assert.assertEquals(history3.get(2).getEntity().getLocale(), retrievedAccount2.getLocale());
+ auditLogsWithHistory = accountDao.getAuditLogsWithHistoryForId(retrievedAccount2.getId(), AuditLevel.FULL, internalCallContext);
+ Assert.assertEquals(auditLogsWithHistory.size(), 3);
+
+ final AccountModelDao history3 = (AccountModelDao) auditLogsWithHistory.get(2).getEntity();
+ Assert.assertEquals(auditLogsWithHistory.get(0).getChangeType(), ChangeType.INSERT);
+ Assert.assertEquals(auditLogsWithHistory.get(1).getChangeType(), ChangeType.UPDATE);
+ Assert.assertEquals(auditLogsWithHistory.get(2).getChangeType(), ChangeType.UPDATE);
+ Assert.assertEquals(history3.getAccountRecordId(), retrievedAccount2.getRecordId());
+ Assert.assertEquals(history3.getTenantRecordId(), retrievedAccount2.getTenantRecordId());
+ Assert.assertEquals(history3.getExternalKey(), retrievedAccount2.getExternalKey());
+ Assert.assertEquals(history3.getMigrated(), retrievedAccount2.getMigrated());
+ Assert.assertEquals(history3.getIsNotifiedForInvoices(), retrievedAccount2.getIsNotifiedForInvoices());
+ Assert.assertEquals(history3.getTimeZone(), retrievedAccount2.getTimeZone());
+ Assert.assertEquals(history3.getLocale(), retrievedAccount2.getLocale());
}
@Test(groups = "slow", description = "Test Account DAO: payment method update")
@@ -399,10 +401,4 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
Assert.assertEquals(auditLogsForAccountEmail2.size(), 1);
Assert.assertEquals(auditLogsForAccountEmail2.get(0).getChangeType(), ChangeType.INSERT);
}
-
- private List<EntityHistoryModelDao<AccountModelDao, Account>> getAccountHistory(final Long accountRecordId) {
- // See https://github.com/killbill/killbill/issues/335
- final AccountSqlDao accountSqlDao = dbi.onDemand(AccountSqlDao.class);
- return accountSqlDao.getHistoryForTargetRecordId(accountRecordId, internalCallContext);
- }
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AuditLogJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AuditLogJson.java
index b79e883..3dde19b 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AuditLogJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AuditLogJson.java
@@ -22,6 +22,8 @@ import org.joda.time.DateTime;
import org.killbill.billing.ObjectType;
import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
+import org.killbill.billing.util.entity.Entity;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -42,6 +44,8 @@ public class AuditLogJson {
private final ObjectType objectType;
private final UUID objectId;
+ private final Entity history;
+
@JsonCreator
public AuditLogJson(@JsonProperty("changeType") final String changeType,
@JsonProperty("changeDate") final DateTime changeDate,
@@ -50,7 +54,8 @@ public class AuditLogJson {
@JsonProperty("changedBy") final String changedBy,
@JsonProperty("reasonCode") final String reasonCode,
@JsonProperty("comments") final String comments,
- @JsonProperty("userToken") final String userToken) {
+ @JsonProperty("userToken") final String userToken,
+ @JsonProperty("history") final Entity history) {
this.changeType = changeType;
this.changeDate = changeDate;
this.changedBy = changedBy;
@@ -59,11 +64,17 @@ public class AuditLogJson {
this.userToken = userToken;
this.objectType = objectType;
this.objectId = objectId;
+ this.history = history;
+ }
+
+ public AuditLogJson(final AuditLogWithHistory auditLogWithHistory) {
+ this(auditLogWithHistory.getChangeType().toString(), auditLogWithHistory.getCreatedDate(), auditLogWithHistory.getAuditedObjectType(), auditLogWithHistory.getAuditedEntityId(), auditLogWithHistory.getUserName(), auditLogWithHistory.getReasonCode(),
+ auditLogWithHistory.getComment(), auditLogWithHistory.getUserToken(), auditLogWithHistory.getEntity());
}
public AuditLogJson(final AuditLog auditLog) {
this(auditLog.getChangeType().toString(), auditLog.getCreatedDate(), auditLog.getAuditedObjectType(), auditLog.getAuditedEntityId(), auditLog.getUserName(), auditLog.getReasonCode(),
- auditLog.getComment(), auditLog.getUserToken());
+ auditLog.getComment(), auditLog.getUserToken(), null);
}
public String getChangeType() {
@@ -98,6 +109,10 @@ public class AuditLogJson {
return objectId;
}
+ public Entity getHistory() {
+ return history;
+ }
+
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index cef00b5..bcb9d1d 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -121,6 +121,7 @@ import org.killbill.billing.util.api.TagDefinitionApiException;
import org.killbill.billing.util.api.TagUserApi;
import org.killbill.billing.util.audit.AccountAuditLogs;
import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.config.definition.JaxrsConfig;
@@ -1501,10 +1502,22 @@ public class AccountResource extends JaxRsResourceBase {
@javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
final TenantContext tenantContext = context.createTenantContextWithAccountId(accountId, request);
final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(accountId, AuditLevel.FULL, tenantContext);
-
return Response.status(Status.OK).entity(getAuditLogs(accountAuditLogs)).build();
}
+ @TimedResource
+ @GET
+ @Path("/{accountId:" + UUID_PATTERN + "}/" + AUDIT_LOG_WITH_HISTORY)
+ @Produces(APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve account audit logs with history by account id", response = AuditLogJson.class, responseContainer = "List")
+ @ApiResponses(value = {@ApiResponse(code = 404, message = "Account not found")})
+ public Response getAccountAuditLogsWithHistory(@PathParam("accountId") final UUID accountId,
+ @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
+ final TenantContext tenantContext = context.createTenantContextWithAccountId(accountId, request);
+ final List<AuditLogWithHistory> auditLogWithHistory = accountUserApi.getAuditLogsWithHistoryForId(accountId, AuditLevel.FULL, tenantContext);
+ return Response.status(Status.OK).entity(getAuditLogsWithHistory(auditLogWithHistory)).build();
+ }
+
private List<AuditLogJson> getAuditLogs(AccountAuditLogs accountAuditLogs) {
if (accountAuditLogs.getAuditLogs() == null) {
return null;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index 3073f8c..c95abc4 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -304,5 +304,6 @@ public interface JaxrsResource {
public static final String QUERY_INCLUDED_DELETED = "includedDeleted";
public static final String AUDIT_LOG = "auditLogs";
+ public static final String AUDIT_LOG_WITH_HISTORY = "auditLogsWithHistory";
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
index dc76f96..78cecc1 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -61,6 +61,7 @@ import org.killbill.billing.entitlement.api.SubscriptionApi;
import org.killbill.billing.entitlement.api.SubscriptionApiException;
import org.killbill.billing.invoice.api.InvoicePayment;
import org.killbill.billing.invoice.api.InvoicePaymentType;
+import org.killbill.billing.jaxrs.json.AuditLogJson;
import org.killbill.billing.jaxrs.json.BillingExceptionJson;
import org.killbill.billing.jaxrs.json.BillingExceptionJson.StackTraceElementJson;
import org.killbill.billing.jaxrs.json.BlockingStateJson;
@@ -89,6 +90,7 @@ import org.killbill.billing.util.api.TagDefinitionApiException;
import org.killbill.billing.util.api.TagUserApi;
import org.killbill.billing.util.audit.AccountAuditLogsForObjectType;
import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.customfield.CustomField;
@@ -724,4 +726,13 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
// If nothing is found, return the latest transaction of given type
return Iterables.getFirst(matchingTransactions, null);
}
+
+ protected List<AuditLogJson> getAuditLogsWithHistory(List<AuditLogWithHistory> auditLogWithHistory) {
+ return ImmutableList.<AuditLogJson>copyOf(Collections2.transform(auditLogWithHistory, new Function<AuditLogWithHistory, AuditLogJson>() {
+ @Override
+ public AuditLogJson apply(@Nullable final AuditLogWithHistory input) {
+ return new AuditLogJson(input);
+ }
+ }));
+ }
}
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestUtils.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestUtils.java
index 3f88ac8..1055887 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestUtils.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestUtils.java
@@ -31,7 +31,7 @@ public abstract class JaxrsTestUtils {
final List<AuditLogJson> auditLogs = new ArrayList<AuditLogJson>();
for (int i = 0; i < 20; i++) {
auditLogs.add(new AuditLogJson(UUIDs.randomUUID().toString(), changeDate, ObjectType.BUNDLE, UUIDs.randomUUID(), UUIDs.randomUUID().toString(),
- UUIDs.randomUUID().toString(), UUIDs.randomUUID().toString(), UUIDs.randomUUID().toString()));
+ UUIDs.randomUUID().toString(), UUIDs.randomUUID().toString(), UUIDs.randomUUID().toString(), null));
}
return auditLogs;
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestAuditLogJson.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestAuditLogJson.java
index 6dce63f..92b77c5 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestAuditLogJson.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestAuditLogJson.java
@@ -44,7 +44,7 @@ public class TestAuditLogJson extends JaxrsTestSuiteNoDB {
final UUID objectId = UUID.randomUUID();
final ObjectType objectType = ObjectType.BUNDLE;
- final AuditLogJson auditLogJson = new AuditLogJson(changeType, changeDate, objectType, objectId, changedBy, reasonCode, comments, userToken);
+ final AuditLogJson auditLogJson = new AuditLogJson(changeType, changeDate, objectType, objectId, changedBy, reasonCode, comments, userToken, null);
Assert.assertEquals(auditLogJson.getChangeType(), changeType);
Assert.assertEquals(auditLogJson.getChangeDate(), changeDate);
Assert.assertEquals(auditLogJson.getChangedBy(), changedBy);
@@ -63,7 +63,8 @@ public class TestAuditLogJson extends JaxrsTestSuiteNoDB {
"\"changedBy\":\"" + auditLogJson.getChangedBy() + "\"," +
"\"reasonCode\":\"" + auditLogJson.getReasonCode() + "\"," +
"\"comments\":\"" + auditLogJson.getComments() + "\"," +
- "\"userToken\":\"" + auditLogJson.getUserToken() + "\"}");
+ "\"userToken\":\"" + auditLogJson.getUserToken() + "\"," +
+ "\"history\":" + auditLogJson.getHistory() + "}");
final AuditLogJson fromJson = mapper.readValue(asJson, AuditLogJson.class);
Assert.assertEquals(fromJson, auditLogJson);
diff --git a/util/src/main/java/org/killbill/billing/util/audit/dao/AuditDao.java b/util/src/main/java/org/killbill/billing/util/audit/dao/AuditDao.java
index 6f19906..49892fe 100644
--- a/util/src/main/java/org/killbill/billing/util/audit/dao/AuditDao.java
+++ b/util/src/main/java/org/killbill/billing/util/audit/dao/AuditDao.java
@@ -22,8 +22,10 @@ import java.util.UUID;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.util.api.AuditLevel;
import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.audit.DefaultAccountAuditLogs;
import org.killbill.billing.util.audit.DefaultAccountAuditLogsForObjectType;
+import org.killbill.billing.util.dao.HistorySqlDao;
import org.killbill.billing.util.dao.TableName;
public interface AuditDao {
@@ -35,4 +37,6 @@ public interface AuditDao {
public DefaultAccountAuditLogsForObjectType getAuditLogsForAccountRecordId(TableName tableName, AuditLevel auditLevel, InternalTenantContext context);
public List<AuditLog> getAuditLogsForId(TableName tableName, UUID objectId, AuditLevel auditLevel, InternalTenantContext context);
+
+ List<AuditLogWithHistory> getAuditLogsWithHistoryForId(HistorySqlDao sqlDao, TableName tableName, UUID objectId, AuditLevel auditLevel, InternalTenantContext context);
}
diff --git a/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java b/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
index 520309d..e454359 100644
--- a/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
+++ b/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
@@ -31,12 +31,16 @@ import org.killbill.billing.ObjectType;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.util.api.AuditLevel;
import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.audit.ChangeType;
import org.killbill.billing.util.audit.DefaultAccountAuditLogs;
import org.killbill.billing.util.audit.DefaultAccountAuditLogsForObjectType;
import org.killbill.billing.util.audit.DefaultAuditLog;
+import org.killbill.billing.util.audit.DefaultAuditLogWithHistory;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.dao.EntityHistoryModelDao;
+import org.killbill.billing.util.dao.HistorySqlDao;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.dao.NonEntitySqlDao;
import org.killbill.billing.util.dao.RecordIdIdMappings;
@@ -49,6 +53,7 @@ import org.killbill.clock.Clock;
import org.skife.jdbi.v2.IDBI;
import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
@@ -173,6 +178,43 @@ public class DefaultAuditDao implements AuditDao {
}
}
+ @Override
+ public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final HistorySqlDao transactional, final TableName tableName, final UUID objectId, final AuditLevel auditLevel, final InternalTenantContext context) {
+ final TableName historyTableName = tableName.getHistoryTableName();
+ if (historyTableName == null) {
+ throw new IllegalStateException("History table shouldn't be null for " + tableName);
+ }
+
+ return transactionalSqlDao.execute(true, new EntitySqlDaoTransactionWrapper<List<AuditLogWithHistory>>() {
+ @Override
+ public List<AuditLogWithHistory> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+ final Long targetRecordId = roNonEntitySqlDao.getRecordIdFromObject(objectId.toString(), tableName.getTableName());
+ final List<EntityHistoryModelDao> objectHistory = transactional.getHistoryForTargetRecordId(targetRecordId, context);
+
+ return ImmutableList.<AuditLogWithHistory>copyOf(Collections2.transform(entitySqlDaoWrapperFactory.become(EntitySqlDao.class).getAuditLogsViaHistoryForTargetRecordId(historyTableName.name(),
+ historyTableName.getTableName().toLowerCase(),
+ targetRecordId,
+ context),
+ new Function<AuditLogModelDao, AuditLogWithHistory>() {
+ @Override
+ public AuditLogWithHistory apply(final AuditLogModelDao inputAuditLog) {
+ EntityHistoryModelDao historyEntity = null;
+ if ( objectHistory != null) {
+ for (EntityHistoryModelDao history : objectHistory) {
+ if (history.getHistoryRecordId() == inputAuditLog.getTargetRecordId()) {
+ historyEntity = history;
+ break;
+ }
+ }
+ }
+
+ return new DefaultAuditLogWithHistory((historyEntity == null ? null : historyEntity.getEntity()), inputAuditLog, tableName.getObjectType(), objectId);
+ }
+ }));
+ }
+ });
+ }
+
private List<AuditLog> doGetAuditLogsForId(final TableName tableName, final UUID objectId, final AuditLevel auditLevel, final InternalTenantContext context) {
final Long recordId = roNonEntitySqlDao.getRecordIdFromObject(objectId.toString(), tableName.getTableName());
if (recordId == null) {
diff --git a/util/src/main/java/org/killbill/billing/util/audit/DefaultAuditLogWithHistory.java b/util/src/main/java/org/killbill/billing/util/audit/DefaultAuditLogWithHistory.java
new file mode 100644
index 0000000..1fa7929
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/audit/DefaultAuditLogWithHistory.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
+ * 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 org.killbill.billing.util.audit;
+
+import java.util.UUID;
+
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.util.audit.dao.AuditLogModelDao;
+import org.killbill.billing.util.entity.Entity;
+
+public class DefaultAuditLogWithHistory<E extends Entity> extends DefaultAuditLog implements AuditLogWithHistory<E> {
+
+ private E entity;
+
+ public DefaultAuditLogWithHistory(final E entity,final AuditLogModelDao auditLogModelDao, final ObjectType objectType, final UUID auditedEntityId) {
+ super(auditLogModelDao, objectType, auditedEntityId);
+ this.entity = entity;
+ }
+
+ @Override
+ public E getEntity() {
+ return this.entity;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("DefaultAuditLogWithHistory{");
+ sb.append("auditLog=").append(super.toString());
+ sb.append(", entity=").append(entity);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ final DefaultAuditLogWithHistory<E> that = (DefaultAuditLogWithHistory<E>) o;
+
+ if (entity != null ? !entity.equals(that.entity) : that.entity != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (entity != null ? entity.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDao.java b/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDao.java
index 447f132..9fee7a9 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDao.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDao.java
@@ -31,16 +31,18 @@ public class EntityHistoryModelDao<M extends EntityModelDao<E>, E extends Entity
private Long targetRecordId;
private M entity;
private ChangeType changeType;
+ private Long historyRecordId;
- public EntityHistoryModelDao(final UUID id, final M src, final Long targetRecordId, final ChangeType type, final DateTime createdDate) {
+ public EntityHistoryModelDao(final UUID id, final M src, final Long targetRecordId, final ChangeType type, final Long historyRecordId, final DateTime createdDate) {
super(id, createdDate, createdDate);
this.changeType = type;
this.targetRecordId = targetRecordId;
this.entity = src;
+ this.historyRecordId = historyRecordId;
}
- public EntityHistoryModelDao(final M src, final Long targetRecordId, final ChangeType type, final DateTime createdDate) {
- this(UUIDs.randomUUID(), src, targetRecordId, type, createdDate);
+ public EntityHistoryModelDao(final M src, final Long targetRecordId, final ChangeType type, final Long historyRecordId, final DateTime createdDate) {
+ this(UUIDs.randomUUID(), src, targetRecordId, type, historyRecordId, createdDate);
}
public ChangeType getChangeType() {
@@ -66,4 +68,12 @@ public class EntityHistoryModelDao<M extends EntityModelDao<E>, E extends Entity
public void setChangeType(final ChangeType changeType) {
this.changeType = changeType;
}
+
+ public Long getHistoryRecordId() {
+ return historyRecordId;
+ }
+
+ public void setHistoryRecordId(final Long historyRecordId) {
+ this.historyRecordId = historyRecordId;
+ }
}
diff --git a/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDaoMapper.java b/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDaoMapper.java
index 5ff331d..49fd117 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDaoMapper.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDaoMapper.java
@@ -44,6 +44,9 @@ public class EntityHistoryModelDaoMapper<M extends EntityModelDao<E>, E extends
final String changeType = r.getString("change_type");
final DateTime createdDate = getDateTime(r, "created_date");
+ // preserve history record id, as it is needed to reference it with audit log
+ final long historyRecordId = r.getLong("history_record_id");
+
final M entityModelDao = entityMapper.map(index, r, ctx);
// Hack -- remove the id as it is the history id, not the entity id
@@ -55,6 +58,6 @@ public class EntityHistoryModelDaoMapper<M extends EntityModelDao<E>, E extends
((EntityModelDaoBase) entityModelDao).setAccountRecordId(targetRecordId);
}
- return new EntityHistoryModelDao(id, entityModelDao, targetRecordId, ChangeType.valueOf(changeType), createdDate);
+ return new EntityHistoryModelDao(id, entityModelDao, targetRecordId, ChangeType.valueOf(changeType), historyRecordId, createdDate);
}
}
diff --git a/util/src/main/java/org/killbill/billing/util/dao/HistorySqlDao.java b/util/src/main/java/org/killbill/billing/util/dao/HistorySqlDao.java
index e1f7cee..23c763e 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/HistorySqlDao.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/HistorySqlDao.java
@@ -21,6 +21,7 @@ package org.killbill.billing.util.dao;
import java.util.List;
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.EntityModelDao;
import org.skife.jdbi.v2.sqlobject.Bind;
@@ -33,7 +34,7 @@ public interface HistorySqlDao<M extends EntityModelDao<E>, E extends Entity> {
@SqlQuery
public List<EntityHistoryModelDao<M, E>> getHistoryForTargetRecordId(@Bind("targetRecordId") final long targetRecordId,
- @SmartBindBean InternalCallContext context);
+ @SmartBindBean InternalTenantContext context);
@SqlUpdate
@GetGeneratedKeys
public Long addHistoryFromTransaction(@EntityHistoryBinder EntityHistoryModelDao<M, E> history,
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 907af4b..e5db8e3 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
@@ -504,7 +504,7 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>,
}
private Long insertHistory(final Long entityRecordId, final M entityModelDao, final ChangeType changeType, final InternalCallContext context) {
- final EntityHistoryModelDao<M, E> history = new EntityHistoryModelDao<M, E>(entityModelDao, entityRecordId, changeType, clock.getUTCNow());
+ final EntityHistoryModelDao<M, E> history = new EntityHistoryModelDao<M, E>(entityModelDao, entityRecordId, changeType, null, clock.getUTCNow());
final Long recordId = sqlDao.addHistoryFromTransaction(history, context);
printSQLWarnings();
return recordId;
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 26faf51..5b861e8 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
@@ -336,6 +336,7 @@ auditTableValues() ::= <<
getHistoryForTargetRecordId() ::= <<
select
<idField("")>
+, <prefix>record_id as history_record_id
, <historyTableFields("t.")>
<accountRecordIdFieldWithComma("t.")>
<tenantRecordIdFieldWithComma("t.")>
diff --git a/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java b/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
index d6798fc..cbbf33a 100644
--- a/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
+++ b/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
@@ -30,6 +30,8 @@ import org.killbill.billing.account.api.AccountEmail;
import org.killbill.billing.account.api.AccountUserApi;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.mock.MockAccountBuilder;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.entity.DefaultPagination;
@@ -187,4 +189,9 @@ public class MockAccountUserApi implements AccountUserApi {
public List<Account> getChildrenAccounts(final UUID uuid, final TenantContext tenantContext) throws AccountApiException {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final UUID uuid, final AuditLevel auditLevel, final TenantContext tenantContext) throws AccountApiException {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/util/src/test/java/org/killbill/billing/util/audit/dao/MockAuditDao.java b/util/src/test/java/org/killbill/billing/util/audit/dao/MockAuditDao.java
index 1aa816d..dc84aeb 100644
--- a/util/src/test/java/org/killbill/billing/util/audit/dao/MockAuditDao.java
+++ b/util/src/test/java/org/killbill/billing/util/audit/dao/MockAuditDao.java
@@ -25,8 +25,10 @@ import java.util.UUID;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.util.api.AuditLevel;
import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.audit.DefaultAccountAuditLogs;
import org.killbill.billing.util.audit.DefaultAccountAuditLogsForObjectType;
+import org.killbill.billing.util.dao.HistorySqlDao;
import org.killbill.billing.util.dao.TableName;
import com.google.common.base.MoreObjects;
@@ -81,4 +83,9 @@ public class MockAuditDao implements AuditDao {
return allAuditLogs;
}
}
+
+ @Override
+ public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final HistorySqlDao sqlDao, final TableName tableName, final UUID objectId, final AuditLevel auditLevel, final InternalTenantContext context) {
+ throw new UnsupportedOperationException();
+ }
}