private List<AuditLog> filterAuditLogs(final AuditLevel auditLevel, final List<AuditLog> auditLogs) { // TODO Do the filtering in the query if (AuditLevel.FULL.equals(auditLevel)) { return auditLogs; } else if (AuditLevel.MINIMAL.equals(auditLevel) && !auditLogs.isEmpty()) { if (ChangeType.INSERT.equals(auditLogs.get(0).getChangeType())) { return ImmutableList.<AuditLog>of(auditLogs.get(0)); } else { // We may be coming here via the history code path - only a single mapped history record id // will be for the initial INSERT return ImmutableList.<AuditLog>of(); } } else if (AuditLevel.NONE.equals(auditLevel)) { return ImmutableList.<AuditLog>of(); } else { return auditLogs; } } }
@Test(groups = "fast") public void testForObject() throws Exception { for (final ObjectType objectType : ObjectType.values()) { for (final UUID objectId : objectIds) { for (final AuditLevel level : AuditLevel.values()) { if (AuditLevel.NONE.equals(level)) { Assert.assertEquals(auditUserApi.getAuditLogs(objectId, objectType, level, callContext).size(), 0); } else if (AuditLevel.MINIMAL.equals(level)) { Assert.assertEquals(auditUserApi.getAuditLogs(objectId, objectType, level, callContext), ImmutableList.<AuditLog>of(auditLogs.get(0))); } else { Assert.assertEquals(auditUserApi.getAuditLogs(objectId, objectType, level, callContext), auditLogs); } } } } } }
@Test(groups = "slow") public void testRetrieveAuditsDirectly() throws Exception { addTag(); // Verify we get an audit entry for the tag_history table final Handle handle = dbi.open(); final String tagHistoryString = (String) handle.select("select id from tag_history limit 1").get(0).get("id"); handle.close(); for (final AuditLevel level : AuditLevel.values()) { final List<AuditLog> auditLogs = auditDao.getAuditLogsForId(TableName.TAG_HISTORY, UUID.fromString(tagHistoryString), level, internalCallContext); verifyAuditLogsForTag(auditLogs, level); } }
@Override public String apply(final AuditLevel input) { return input.toString(); } }));
@Override public int hashCode() { return level != null ? level.hashCode() : 0; } }
public AuditMode(final String auditModeString) { this.level = AuditLevel.valueOf(auditModeString.toUpperCase()); }
@Test(groups = "slow") public void testRetrieveAuditsViaHistory() throws Exception { addTag(); for (final AuditLevel level : AuditLevel.values()) { final List<AuditLog> auditLogs = auditDao.getAuditLogsForId(TableName.TAG, tag.getId(), level, internalCallContext); verifyAuditLogsForTag(auditLogs, level); final AccountAuditLogs accountAuditLogs = auditDao.getAuditLogsForAccountRecordId(level, internalCallContext); verifyAuditLogsForTag(accountAuditLogs.getAuditLogs(ObjectType.TAG).getAuditLogs(tag.getId()), level); final AccountAuditLogsForObjectType accountAuditLogsForObjectType = auditDao.getAuditLogsForAccountRecordId(TableName.TAG, level, internalCallContext); verifyAuditLogsForTag(accountAuditLogsForObjectType.getAuditLogs(tag.getId()), level); } }
@TimedResource @GET @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}") @Produces(APPLICATION_JSON) @ApiOperation(value = "Search accounts", response = AccountJson.class, responseContainer = "List") @ApiResponses(value = {}) public Response searchAccounts(@PathParam("searchKey") final String searchKey, @QueryParam(QUERY_SEARCH_OFFSET) @DefaultValue("0") final Long offset, @QueryParam(QUERY_SEARCH_LIMIT) @DefaultValue("100") final Long limit, @QueryParam(QUERY_ACCOUNT_WITH_BALANCE) @DefaultValue("false") final Boolean accountWithBalance, @QueryParam(QUERY_ACCOUNT_WITH_BALANCE_AND_CBA) @DefaultValue("false") final Boolean accountWithBalanceAndCBA, @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode, @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException { final TenantContext tenantContext = context.createTenantContextNoAccountId(request); final Pagination<Account> accounts = accountUserApi.searchAccounts(searchKey, offset, limit, tenantContext); final URI nextPageUri = uriBuilder.nextPage(AccountResource.class, "searchAccounts", accounts.getNextOffset(), limit, ImmutableMap.<String, String>of("searchKey", searchKey, QUERY_ACCOUNT_WITH_BALANCE, accountWithBalance.toString(), QUERY_ACCOUNT_WITH_BALANCE_AND_CBA, accountWithBalanceAndCBA.toString(), QUERY_AUDIT, auditMode.getLevel().toString())); return buildStreamingPaginationResponse(accounts, new Function<Account, AccountJson>() { @Override public AccountJson apply(final Account account) { final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(account.getId(), auditMode.getLevel(), tenantContext); return getAccount(account, accountWithBalance, accountWithBalanceAndCBA, accountAuditLogs, tenantContext); } }, nextPageUri ); }
@Override public List<AuditLog> getAuditLogs(final UUID objectId, final ObjectType objectType, final AuditLevel auditLevel, final TenantContext context) { // Optimization - bail early if (AuditLevel.NONE.equals(auditLevel)) { return ImmutableList.<AuditLog>of(); } final TableName tableName = getTableNameFromObjectType(objectType); if (tableName == null) { return ImmutableList.<AuditLog>of(); } return auditDao.getAuditLogsForId(tableName, objectId, auditLevel, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)); }
@Test(groups = "fast") public void testForObject() throws Exception { for (final ObjectType objectType : ObjectType.values()) { for (final UUID objectId : objectIds) { for (final AuditLevel level : AuditLevel.values()) { if (AuditLevel.NONE.equals(level)) { Assert.assertEquals(auditUserApi.getAuditLogs(objectId, objectType, level, callContext).size(), 0); } else if (AuditLevel.MINIMAL.equals(level)) { Assert.assertEquals(auditUserApi.getAuditLogs(objectId, objectType, level, callContext), ImmutableList.<AuditLog>of(auditLogs.get(0))); } else { Assert.assertEquals(auditUserApi.getAuditLogs(objectId, objectType, level, callContext), auditLogs); } } } } } }
@Test(groups = "slow") public void testRetrieveAuditsDirectly() throws Exception { addTag(); // Verify we get an audit entry for the tag_history table final Handle handle = dbi.open(); final String tagHistoryString = (String) handle.select("select id from tag_history limit 1").get(0).get("id"); handle.close(); for (final AuditLevel level : AuditLevel.values()) { final List<AuditLog> auditLogs = auditDao.getAuditLogsForId(TableName.TAG_HISTORY, UUID.fromString(tagHistoryString), level, internalCallContext); verifyAuditLogsForTag(auditLogs, level); } }
@TimedResource @GET @Path("/" + PAGINATION) @Produces(APPLICATION_JSON) @ApiOperation(value = "List accounts", response = AccountJson.class, responseContainer = "List") @ApiResponses(value = {}) public Response getAccounts(@QueryParam(QUERY_SEARCH_OFFSET) @DefaultValue("0") final Long offset, @QueryParam(QUERY_SEARCH_LIMIT) @DefaultValue("100") final Long limit, @QueryParam(QUERY_ACCOUNT_WITH_BALANCE) @DefaultValue("false") final Boolean accountWithBalance, @QueryParam(QUERY_ACCOUNT_WITH_BALANCE_AND_CBA) @DefaultValue("false") final Boolean accountWithBalanceAndCBA, @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode, @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException { final TenantContext tenantContext = context.createTenantContextNoAccountId(request); final Pagination<Account> accounts = accountUserApi.getAccounts(offset, limit, tenantContext); final URI nextPageUri = uriBuilder.nextPage(AccountResource.class, "getAccounts", accounts.getNextOffset(), limit, ImmutableMap.<String, String>of(QUERY_ACCOUNT_WITH_BALANCE, accountWithBalance.toString(), QUERY_ACCOUNT_WITH_BALANCE_AND_CBA, accountWithBalanceAndCBA.toString(), QUERY_AUDIT, auditMode.getLevel().toString())); return buildStreamingPaginationResponse(accounts, new Function<Account, AccountJson>() { @Override public AccountJson apply(final Account account) { final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(account.getId(), auditMode.getLevel(), tenantContext); return getAccount(account, accountWithBalance, accountWithBalanceAndCBA, accountAuditLogs, tenantContext); } }, nextPageUri ); }
@Override public List<AuditLog> getAuditLogsForId(final TableName tableName, final UUID objectId, final AuditLevel auditLevel, final InternalTenantContext context) { final Map<UUID, List<AuditLog>> auditLogsForTableName = auditLogsForTables.get(tableName); if (auditLogsForTableName == null) { return ImmutableList.<AuditLog>of(); } final List<AuditLog> auditLogsForObjectId = auditLogsForTableName.get(objectId); final List<AuditLog> allAuditLogs = MoreObjects.firstNonNull(auditLogsForObjectId, ImmutableList.<AuditLog>of()); if (AuditLevel.FULL.equals(auditLevel)) { return allAuditLogs; } else if (AuditLevel.MINIMAL.equals(auditLevel) && allAuditLogs.size() > 0) { return ImmutableList.<AuditLog>of(allAuditLogs.get(0)); } else if (AuditLevel.NONE.equals(auditLevel)) { return ImmutableList.<AuditLog>of(); } else { return allAuditLogs; } }
qp.setRequired(false); qp.setType("string"); final List<String> values = ImmutableList.copyOf(Iterables.transform(ImmutableList.<AuditLevel>copyOf(AuditLevel.values()), new Function<AuditLevel, String>() { @Override public String apply(final AuditLevel input) {
@TimedResource @GET @Path("/" + PAGINATION) @Produces(APPLICATION_JSON) @ApiOperation(value = "List custom fields", response = CustomFieldJson.class, responseContainer = "List") @ApiResponses(value = {}) public Response getCustomFields(@QueryParam(QUERY_SEARCH_OFFSET) @DefaultValue("0") final Long offset, @QueryParam(QUERY_SEARCH_LIMIT) @DefaultValue("100") final Long limit, @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode, @javax.ws.rs.core.Context final HttpServletRequest request) throws CustomFieldApiException { final TenantContext tenantContext = context.createTenantContextNoAccountId(request); final Pagination<CustomField> customFields = customFieldUserApi.getCustomFields(offset, limit, tenantContext); final URI nextPageUri = uriBuilder.nextPage(CustomFieldResource.class, "getCustomFields", customFields.getNextOffset(), limit, ImmutableMap.<String, String>of(QUERY_AUDIT, auditMode.getLevel().toString())); return buildStreamingPaginationResponse(customFields, new Function<CustomField, CustomFieldJson>() { @Override public CustomFieldJson apply(final CustomField customField) { // TODO Really slow - we should instead try to figure out the account id final List<AuditLog> auditLogs = auditUserApi.getAuditLogs(customField.getId(), ObjectType.CUSTOM_FIELD, auditMode.getLevel(), tenantContext); return new CustomFieldJson(customField, auditLogs); } }, nextPageUri); }
@Override public AccountAuditLogs getAccountAuditLogs(final UUID accountId, final AuditLevel auditLevel, final TenantContext tenantContext) { // Optimization - bail early if (AuditLevel.NONE.equals(auditLevel)) { return new DefaultAccountAuditLogs(accountId); } return auditDao.getAuditLogsForAccountRecordId(auditLevel, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext)); }
@Test(groups = "slow") public void testRetrieveAuditsViaHistory() throws Exception { addTag(); for (final AuditLevel level : AuditLevel.values()) { final List<AuditLog> auditLogs = auditDao.getAuditLogsForId(TableName.TAG, tag.getId(), level, internalCallContext); verifyAuditLogsForTag(auditLogs, level); final AccountAuditLogs accountAuditLogs = auditDao.getAuditLogsForAccountRecordId(level, internalCallContext); verifyAuditLogsForTag(accountAuditLogs.getAuditLogs(ObjectType.TAG).getAuditLogs(tag.getId()), level); final AccountAuditLogsForObjectType accountAuditLogsForObjectType = auditDao.getAuditLogsForAccountRecordId(TableName.TAG, level, internalCallContext); verifyAuditLogsForTag(accountAuditLogsForObjectType.getAuditLogs(tag.getId()), level); } }
@TimedResource @GET @Path("/" + PAGINATION) @Produces(APPLICATION_JSON) @ApiOperation(value = "List invoices", response = InvoiceJson.class, responseContainer = "List") @ApiResponses(value = {}) public Response getInvoices(@QueryParam(QUERY_SEARCH_OFFSET) @DefaultValue("0") final Long offset, @QueryParam(QUERY_SEARCH_LIMIT) @DefaultValue("100") final Long limit, @QueryParam(QUERY_INVOICE_WITH_ITEMS) @DefaultValue("false") final Boolean withItems, @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode, @javax.ws.rs.core.Context final HttpServletRequest request) throws InvoiceApiException { final TenantContext tenantContext = context.createTenantContextNoAccountId(request); final Pagination<Invoice> invoices = invoiceApi.getInvoices(offset, limit, tenantContext); final URI nextPageUri = uriBuilder.nextPage(InvoiceResource.class, "getInvoices", invoices.getNextOffset(), limit, ImmutableMap.<String, String>of(QUERY_INVOICE_WITH_ITEMS, withItems.toString(), QUERY_AUDIT, auditMode.getLevel().toString())); final AtomicReference<Map<UUID, AccountAuditLogs>> accountsAuditLogs = new AtomicReference<Map<UUID, AccountAuditLogs>>(new HashMap<UUID, AccountAuditLogs>()); return buildStreamingPaginationResponse(invoices, new Function<Invoice, InvoiceJson>() { @Override public InvoiceJson apply(final Invoice invoice) { // Cache audit logs per account if (accountsAuditLogs.get().get(invoice.getAccountId()) == null) { accountsAuditLogs.get().put(invoice.getAccountId(), auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext)); } return new InvoiceJson(invoice, withItems, null, accountsAuditLogs.get().get(invoice.getAccountId())); } }, nextPageUri ); }
@Override public AccountAuditLogsForObjectType getAccountAuditLogs(final UUID accountId, final ObjectType objectType, final AuditLevel auditLevel, final TenantContext tenantContext) { // Optimization - bail early if (AuditLevel.NONE.equals(auditLevel)) { return new DefaultAccountAuditLogsForObjectType(auditLevel); } final TableName tableName = getTableNameFromObjectType(objectType); if (tableName == null) { return new DefaultAccountAuditLogsForObjectType(auditLevel); } return auditDao.getAuditLogsForAccountRecordId(tableName, auditLevel, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext)); }
final URI nextPageUri = uriBuilder.nextPage(InvoiceResource.class, "searchInvoices", invoices.getNextOffset(), limit, ImmutableMap.<String, String>of("searchKey", searchKey, QUERY_INVOICE_WITH_ITEMS, withItems.toString(), QUERY_AUDIT, auditMode.getLevel().toString())); final AtomicReference<Map<UUID, AccountAuditLogs>> accountsAuditLogs = new AtomicReference<Map<UUID, AccountAuditLogs>>(new HashMap<UUID, AccountAuditLogs>()); return buildStreamingPaginationResponse(invoices,