Skip to content

Commit

Permalink
Added REST APIs for retrieving the list of task log entries [comixed#413
Browse files Browse the repository at this point in the history
]
  • Loading branch information
mcpierce committed Jul 22, 2020
1 parent 8fa7795 commit bf76ef8
Show file tree
Hide file tree
Showing 10 changed files with 296 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@

package org.comixedproject.model.tasks;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonView;
import java.util.Date;
import java.util.Objects;
import javax.persistence.*;
import lombok.Getter;
import lombok.Setter;
import org.comixed.views.View;

/**
* <code>TaskAuditLogEntry</code> represents a single entry in the task audit log table.
Expand All @@ -41,21 +45,31 @@ public class TaskAuditLogEntry {
@Column(name = "start_time", nullable = false, updatable = false)
@Getter
@Setter
@JsonProperty("startTime")
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
@JsonView(View.TaskAuditLogEntryList.class)
private Date startTime = new Date();

@Column(name = "end_time", nullable = false, updatable = false)
@Getter
@Setter
@JsonProperty("endTime")
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
@JsonView(View.TaskAuditLogEntryList.class)
private Date endTime = new Date();

@Column(name = "successful", nullable = false, updatable = false)
@Getter
@Setter
@JsonProperty("successful")
@JsonView(View.TaskAuditLogEntryList.class)
private Boolean successful;

@Column(name = "description", nullable = false, updatable = false, length = 2048)
@Getter
@Setter
@JsonProperty("description")
@JsonView(View.TaskAuditLogEntryList.class)
private String description;

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,7 @@ public interface LibraryUpdate {}

/** Used when viewing the list of plugins. */
public interface PluginList {}

/** Uses when viewing the list of task audit log entries. */
public interface TaskAuditLogEntryList {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* 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.controller;

/**
* <code>ComiXedControllerException</code> is throw when an error occurs during the processing of a
* REST API request.
*
* @author Darryl L. Pierce
*/
public class ComiXedControllerException extends Exception {
public ComiXedControllerException(final String message, final Exception cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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.controller.tasks;

import com.fasterxml.jackson.annotation.JsonView;
import java.util.Date;
import java.util.List;
import lombok.extern.log4j.Log4j2;
import org.comixed.controller.ComiXedControllerException;
import org.comixed.model.tasks.TaskAuditLogEntry;
import org.comixed.repositories.tasks.TaskAuditLogRepository;
import org.comixed.service.ComiXedServiceException;
import org.comixed.service.task.TaskService;
import org.comixed.views.View;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* <code>TaskController</code> provides REST APIs for interacting with the tasks system.
*
* @author Darryl L. Pierce
*/
@RestController
@RequestMapping(value = "/api/tasks")
@Log4j2
public class TaskController {
@Autowired private TaskAuditLogRepository taskAuditLogRepository;
@Autowired private TaskService taskService;

/**
* Retrieve the list of log entries after the cutoff time.
*
* @param cutoff the cutoff
* @return the log entries
*/
@GetMapping(value = "/entries/{cutoff}", produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasRole('ADMIN')")
@JsonView(View.TaskAuditLogEntryList.class)
public List<TaskAuditLogEntry> getAllAfterDate(@PathVariable("cutoff") final Long timestamp)
throws ComiXedControllerException {
final Date cutoff = new Date(timestamp);
log.debug("Getting all task audit log entries after: {}", cutoff);

try {
return this.taskService.getAuditLogEntriesAfter(cutoff);
} catch (ComiXedServiceException error) {
throw new ComiXedControllerException("unable to get task audit log entries", error);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public class ComicVineScraperController {
* @param volume the volume id
* @param request the request body
* @return the issue
* @throws RESTException if an error occurs
* @throws ComiXedControllerException if an error occurs
*/
@PostMapping(
value = "/volumes/{volume}/issues",
Expand All @@ -64,7 +64,7 @@ public class ComicVineScraperController {
public ScrapingIssue queryForIssue(
@PathVariable("volume") final Integer volume,
@RequestBody() final GetScrapingIssueRequest request)
throws RESTException {
throws ComiXedControllerException {
String issue = request.getIssueNumber();
boolean skipCache = request.isSkipCache();
String apiKey = request.getApiKey();
Expand All @@ -75,7 +75,7 @@ public ScrapingIssue queryForIssue(
try {
return this.scrapingAdaptor.getIssue(apiKey, volume, issue, skipCache);
} catch (ScrapingException error) {
throw new RESTException("Failed to get single scraping issue", error);
throw new ComiXedControllerException("Failed to get single scraping issue", error);
}
}

Expand All @@ -84,14 +84,14 @@ public ScrapingIssue queryForIssue(
*
* @param request the reqwuest body
* @return the list of volumes
* @throws RESTException if an error occurs
* @throws ComiXedControllerException if an error occurs
*/
@PostMapping(
value = "/volumes",
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public List<ScrapingVolume> queryForVolumes(@RequestBody() final GetVolumesRequest request)
throws RESTException {
throws ComiXedControllerException {
String apiKey = request.getApiKey();
boolean skipCache = request.getSkipCache();
String series = request.getSeries();
Expand All @@ -105,7 +105,7 @@ public List<ScrapingVolume> queryForVolumes(@RequestBody() final GetVolumesReque

return result;
} catch (ScrapingException error) {
throw new RESTException("Failed to get list of volumes", error);
throw new ComiXedControllerException("Failed to get list of volumes", error);
}
}

Expand All @@ -116,7 +116,7 @@ public List<ScrapingVolume> queryForVolumes(@RequestBody() final GetVolumesReque
* @param issueId the issue id
* @param request the request body
* @return the scraped and updaed {@link Comic}
* @throws RESTException if an error occurs
* @throws ComiXedControllerException if an error occurs
*/
@PostMapping(
value = "/comics/{comicId}/issue/{issueId}",
Expand All @@ -127,7 +127,7 @@ public Comic scrapeAndSaveComicDetails(
@PathVariable("comicId") final Long comicId,
@PathVariable("issueId") final String issueId,
@RequestBody() final ComicScrapeRequest request)
throws RESTException {
throws ComiXedControllerException {
boolean skipCache = request.getSkipCache();
String apiKey = request.getApiKey();

Expand All @@ -138,7 +138,7 @@ public Comic scrapeAndSaveComicDetails(
try {
comic = this.comicService.getComic(comicId);
} catch (ComicException error) {
throw new RESTException("Failed to load comic", error);
throw new ComiXedControllerException("Failed to load comic", error);
}

try {
Expand All @@ -150,7 +150,7 @@ public Comic scrapeAndSaveComicDetails(

return comic;
} catch (ScrapingException error) {
throw new RESTException("Failed to scrape comic", error);
throw new ComiXedControllerException("Failed to scrape comic", error);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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.controller.tasks;

import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertSame;

import java.util.Date;
import java.util.List;
import org.comixed.controller.ComiXedControllerException;
import org.comixed.model.tasks.TaskAuditLogEntry;
import org.comixed.service.ComiXedServiceException;
import org.comixed.service.task.TaskService;
import org.comixed.service.user.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class TaskControllerTest {
private static final Date TEST_LAST_UPDATED_DATE = new Date();
private static final String TEST_USER_EMAIL = "user@domain.tld";

@InjectMocks private TaskController taskController;
@Mock private UserService userService;
@Mock private TaskService taskService;
@Mock private List<TaskAuditLogEntry> auditLogEntries;

@Test
public void testGetAllEntries() throws ComiXedServiceException, ComiXedControllerException {
Mockito.when(taskService.getAuditLogEntriesAfter(Mockito.any(Date.class)))
.thenReturn(auditLogEntries);

final List<TaskAuditLogEntry> result =
taskController.getAllAfterDate(TEST_LAST_UPDATED_DATE.getTime());

assertNotNull(result);
assertSame(auditLogEntries, result);

Mockito.verify(taskService, Mockito.times(1)).getAuditLogEntriesAfter(TEST_LAST_UPDATED_DATE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ public class ComicVineScraperControllerTest {
@Mock private ComicService comicService;
@Mock private Comic comic;

@Test(expected = RESTException.class)
public void testQueryForVolumesAdaptorRaisesException() throws ScrapingException, RESTException {
@Test(expected = ComiXedControllerException.class)
public void testQueryForVolumesAdaptorRaisesException()
throws ScrapingException, ComiXedControllerException {
Mockito.when(
scrapingAdaptor.getVolumes(
Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean()))
Expand All @@ -75,7 +76,7 @@ public void testQueryForVolumesAdaptorRaisesException() throws ScrapingException
}

@Test
public void testQueryForVolumes() throws RESTException, ScrapingException {
public void testQueryForVolumes() throws ComiXedControllerException, ScrapingException {
Mockito.when(
scrapingAdaptor.getVolumes(
Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean()))
Expand All @@ -91,7 +92,7 @@ public void testQueryForVolumes() throws RESTException, ScrapingException {
}

@Test
public void testQueryForVolumesSkipCache() throws ScrapingException, RESTException {
public void testQueryForVolumesSkipCache() throws ScrapingException, ComiXedControllerException {
Mockito.when(
scrapingAdaptor.getVolumes(
Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean()))
Expand All @@ -106,8 +107,9 @@ public void testQueryForVolumesSkipCache() throws ScrapingException, RESTExcepti
.getVolumes(TEST_API_KEY, TEST_SERIES_NAME, true);
}

@Test(expected = RESTException.class)
public void testQueryForIssueAdaptorRaisesException() throws ScrapingException, RESTException {
@Test(expected = ComiXedControllerException.class)
public void testQueryForIssueAdaptorRaisesException()
throws ScrapingException, ComiXedControllerException {
Mockito.when(
scrapingAdaptor.getIssue(
Mockito.anyString(), Mockito.anyInt(), Mockito.anyString(), Mockito.anyBoolean()))
Expand All @@ -124,7 +126,7 @@ public void testQueryForIssueAdaptorRaisesException() throws ScrapingException,
}

@Test
public void testQueryForIssue() throws ScrapingException, RESTException {
public void testQueryForIssue() throws ScrapingException, ComiXedControllerException {
Mockito.when(
scrapingAdaptor.getIssue(
Mockito.anyString(), Mockito.anyInt(), Mockito.anyString(), Mockito.anyBoolean()))
Expand All @@ -142,8 +144,9 @@ public void testQueryForIssue() throws ScrapingException, RESTException {
.getIssue(TEST_API_KEY, TEST_VOLUME, TEST_ISSUE_NUMBER, TEST_SKIP_CACHE);
}

@Test(expected = RESTException.class)
public void testScrapeAndSaveComicDetailsNoSuchComic() throws ComicException, RESTException {
@Test(expected = ComiXedControllerException.class)
public void testScrapeAndSaveComicDetailsNoSuchComic()
throws ComicException, ComiXedControllerException {
Mockito.when(comicService.getComic(Mockito.anyLong())).thenThrow(ComicException.class);

try {
Expand All @@ -154,9 +157,9 @@ public void testScrapeAndSaveComicDetailsNoSuchComic() throws ComicException, RE
}
}

@Test(expected = RESTException.class)
@Test(expected = ComiXedControllerException.class)
public void testScrapeAndSaveComicDetailsScrapingAdaptorRaisesException()
throws ComicException, ScrapingException, RESTException {
throws ComicException, ScrapingException, ComiXedControllerException {
Mockito.when(comicService.getComic(Mockito.anyLong())).thenReturn(comic);
Mockito.doThrow(ScrapingException.class)
.when(scrapingAdaptor)
Expand All @@ -177,7 +180,7 @@ public void testScrapeAndSaveComicDetailsScrapingAdaptorRaisesException()

@Test
public void testScrapeAndSaveComicDetails()
throws ComicException, ScrapingException, RESTException {
throws ComicException, ScrapingException, ComiXedControllerException {
Mockito.when(comicService.getComic(Mockito.anyLong())).thenReturn(comic);

Comic result =
Expand Down

0 comments on commit bf76ef8

Please sign in to comment.