Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add audit log entries as tasks complete #414

Merged
merged 2 commits into from
Jul 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion comixed-library/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>2.5.3</version>
<version>2.7.0</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* ComiXed - A digital comic book library management application.
* Copyright (C) 2020, The ComiXed Project
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses>
*/

package org.comixed.model.tasks;

import java.util.Date;
import java.util.Objects;
import javax.persistence.*;
import lombok.Getter;
import lombok.Setter;

/**
* <code>TaskAuditLogEntry</code> represents a single entry in the task audit log table.
*
* @author Darryl L. Pierce
*/
@Entity
@Table(name = "task_audit_log")
public class TaskAuditLogEntry {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Getter
@Setter
private Long id;

@Column(name = "start_time", nullable = false, updatable = false)
@Getter
@Setter
private Date startTime = new Date();

@Column(name = "end_time", nullable = false, updatable = false)
@Getter
@Setter
private Date endTime = new Date();

@Column(name = "successful", nullable = false, updatable = false)
@Getter
@Setter
private Boolean successful;

@Column(name = "description", nullable = false, updatable = false, length = 2048)
@Getter
@Setter
private String description;

@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final TaskAuditLogEntry that = (TaskAuditLogEntry) o;
return id.equals(that.id)
&& startTime.equals(that.startTime)
&& endTime.equals(that.endTime)
&& successful.equals(that.successful)
&& description.equals(that.description);
}

@Override
public int hashCode() {
return Objects.hash(id, startTime, endTime, successful, description);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* ComiXed - A digital comic book library management application.
* Copyright (C) 2020, The ComiXed Project
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses>
*/

package org.comixed.repositories.tasks;

import java.util.Date;
import java.util.List;
import org.comixed.model.tasks.TaskAuditLogEntry;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/**
* <code>TaskAuditLogRepository</code> handles storing and fetching instances of {@link
* TaskAuditLogEntry}.
*
* @author Darryl L. Pierce
*/
@Repository
public interface TaskAuditLogRepository extends JpaRepository<TaskAuditLogEntry, Long> {
/**
* Returns the first set of entries after the specified start date.
*
* @param startTime the earliest startTime date
* @return the log entries
*/
List<TaskAuditLogEntry> findAllByStartTimeGreaterThan(Date startTime);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>

<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="004_issue-204_add_task_audit_log.xml"
author="mcpierce">

<createTable tableName="task_audit_log">
<column name="id"
type="bigint">
<constraints nullable="false"
unique="true"
primaryKey="true"/>
</column>
<column name="start_time"
type="timestamp">
<constraints nullable="false"/>
</column>
<column name="end_time"
type="timestamp"
defaultValueComputed="NOW()">
<constraints nullable="false"/>
</column>
<column name="successful"
type="boolean">
<constraints nullable="false"/>
</column>
<column name="description"
type="varchar(2048)">
<constraints nullable="false"/>
</column>
</createTable>

<addAutoIncrement tableName="task_audit_log"
columnName="id"
columnDataType="bigint"
incrementBy="1"
startWith="1"/>

<createIndex tableName="task_audit_log"
indexName="task_audit_log_start_time_idx">
<column name="start_time"/>
</createIndex>

<createIndex tableName="task_audit_log"
indexName="task_audit_log_successful_idx">
<column name="successful"/>
</createIndex>

</changeSet>
</databaseChangeLog>
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@

<include file="/db/migrations/0.7.0/001_issue-13_add_comic_file_entries_table.xml"/>
<include file="/db/migrations/0.7.0/002_issue-356_add_created_date_to_last_read_date_table.xml"/>
<include file="/db/migrations/0.7.0/004_issue-204_add_task_audit_log.xml"/>

</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* ComiXed - A digital comic book library management application.
* Copyright (C) 2020, The ComiXed Project
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses>
*/

package org.comixed.repositories.tasks;

import static junit.framework.TestCase.*;

import com.github.springtestdbunit.DbUnitTestExecutionListener;
import com.github.springtestdbunit.annotation.DatabaseSetup;
import java.util.Date;
import java.util.List;
import java.util.Random;
import org.comixed.model.tasks.TaskAuditLogEntry;
import org.comixed.repositories.RepositoryContext;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = RepositoryContext.class)
@TestPropertySource(locations = "classpath:application.properties")
@DatabaseSetup("classpath:test-database.xml")
@TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class
})
public class TaskAuditLogRepositoryTest {
private static final Random RANDOM = new Random();
private static final int TEST_LOG_ENTRY_RESULT_SIZE = 5;
private static final Date TEST_START_TIMESTAMP =
new Date(System.currentTimeMillis() - 58L * 60L * 1000L);
private static final Date TEST_STARTED = new Date(System.currentTimeMillis() - 3L * 60L * 1000L);
private static final Date TEST_ENDED = new Date();
private static final Boolean TEST_SUCCESSFUL = RANDOM.nextBoolean();
private static final String TEST_DESCRIPTION = "The task description";

@Autowired private TaskAuditLogRepository repository;

@Test
public void testGetEntriesEarliestStartTime() {
final List<TaskAuditLogEntry> result =
this.repository.findAllByStartTimeGreaterThan(new Date(0L));

assertNotNull(result);
assertFalse(result.isEmpty());
assertEquals(TEST_LOG_ENTRY_RESULT_SIZE, result.size());
for (int index = 0; index < result.size() - 1; index++) {
assertTrue(
result.get(index).getStartTime().getTime()
< result.get(index + 1).getStartTime().getTime());
}
}

@Test
public void testGetEntriesAfterSpecifiedDate() {
final List<TaskAuditLogEntry> result =
this.repository.findAllByStartTimeGreaterThan(TEST_START_TIMESTAMP);

assertNotNull(result);
assertFalse(result.isEmpty());
for (int index = 0; index < result.size() - 1; index++) {
assertTrue(TEST_START_TIMESTAMP.getTime() < result.get(index).getStartTime().getTime());
assertTrue(
result.get(index).getStartTime().getTime()
< result.get(index + 1).getStartTime().getTime());
}
}

@Test
public void testSaveEntry() {
final TaskAuditLogEntry entry = new TaskAuditLogEntry();
entry.setStartTime(TEST_STARTED);
entry.setEndTime(TEST_ENDED);
entry.setSuccessful(TEST_SUCCESSFUL);
entry.setDescription(TEST_DESCRIPTION);

final TaskAuditLogEntry result = repository.save(entry);

assertNotNull(result);
assertNotNull(result.getId());
assertEquals(TEST_STARTED, result.getStartTime());
assertEquals(TEST_ENDED, result.getEndTime());
assertEquals(TEST_SUCCESSFUL, result.getSuccessful());
assertEquals(TEST_DESCRIPTION, result.getDescription());

final List<TaskAuditLogEntry> entries =
repository.findAllByStartTimeGreaterThan(TEST_START_TIMESTAMP);

assertTrue(entries.contains(result));
}
}
25 changes: 25 additions & 0 deletions comixed-library/src/test/resources/test-database.xml
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,31 @@
<tasks id="1002"
task_type="ADD_COMIC"
created="2020-02-19 08:24:00"/>
<task_audit_log id="1000"
start_time="[now-1d]"
end_time="[now-1d+1m]"
successful="true"
description="First task entry"/>
<task_audit_log id="1001"
start_time="[now-1h]"
end_time="[now-59m]"
successful="true"
description="Second task entry"/>
<task_audit_log id="1002"
start_time="[now-59m]"
end_time="[now-58m]"
successful="false"
description="Third task entry"/>
<task_audit_log id="1003"
start_time="[now-58m]"
end_time="[now-57m]"
successful="false"
description="Fourth task entry"/>
<task_audit_log id="1004"
start_time="[now-57m]"
end_time="[now-56m]"
successful="true"
description="Fifth task entry"/>
<comic_characters comic_id="1000"
character_name="Captain America"/>
<comic_characters comic_id="1000"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@

package org.comixed.task.runner;

import java.util.Date;
import lombok.extern.log4j.Log4j2;
import org.comixed.task.adaptors.TaskAdaptor;
import org.comixed.model.tasks.TaskAuditLogEntry;
import org.comixed.repositories.tasks.TaskAuditLogRepository;
import org.comixed.task.model.MonitorTaskQueue;
import org.comixed.task.model.WorkerTask;
import org.comixed.task.model.WorkerTaskException;
import org.springframework.beans.factory.InitializingBean;
Expand All @@ -31,19 +34,33 @@
@Log4j2
public class TaskManager implements InitializingBean {
@Autowired private ThreadPoolTaskExecutor taskExecutor;
@Autowired private TaskAdaptor taskAdaptor;
@Autowired private TaskAuditLogRepository auditLogRepository;

public void runTask(final WorkerTask task) {
this.taskExecutor.execute(
() -> {
log.debug("Preparing to run task: {}", task.getDescription());
final String description = task.getDescription();
log.debug("Preparing to run task: {}", description);
final Date started = new Date();
boolean success = false;
try {
task.startTask();
success = true;
} catch (WorkerTaskException error) {
log.error("Error executing task: {}" + task.getDescription(), error);
log.error("Error executing task: {}" + description, error);
} finally {
task.afterExecution();
}
final Date ended = new Date();
// do not log MonitorTaskQueue events
if (!(task instanceof MonitorTaskQueue)) {
final TaskAuditLogEntry entry = new TaskAuditLogEntry();
entry.setStartTime(started);
entry.setEndTime(ended);
entry.setSuccessful(success);
entry.setDescription(description);
TaskManager.this.auditLogRepository.save(entry);
}
});
}

Expand Down