Skip to content

Commit

Permalink
Added REST APIs for moving the comic library [comixed#21]
Browse files Browse the repository at this point in the history
 * Added a task to create the individual mover tasks.
  • Loading branch information
mcpierce committed Jun 30, 2020
1 parent 5954292 commit 0548c7b
Show file tree
Hide file tree
Showing 15 changed files with 561 additions and 4 deletions.
Expand Up @@ -29,5 +29,6 @@ public enum TaskType {
RESCAN_COMIC,
DELETE_COMIC,
DELETE_COMICS,
CONVERT_COMIC;
CONVERT_COMIC,
MOVE_COMIC;
}
Expand Up @@ -21,6 +21,7 @@
import java.util.Date;
import java.util.List;
import org.comixed.model.comic.Comic;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
Expand Down Expand Up @@ -103,4 +104,7 @@ List<Comic> findIssuesAfterComic(

@Query("SELECT c FROM Comic c WHERE c.dateDeleted IS NOT NULL")
List<Comic> findAllMarkedForDeletion();

@Query("SELECT c FROM Comic c")
List<Comic> findComicsToMove(PageRequest page);
}
Expand Up @@ -166,4 +166,20 @@ public ClearImageCacheResponse clearImageCache() {

return new ClearImageCacheResponse(true);
}

@PostMapping(
value = "/library/move",
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public MoveComicsResponse moveComics(@RequestBody() MoveComicsRequest request) {
String targetDirectory = request.getTargetDirectory();
String renamingRule = request.getRenamingRule();
Boolean deletePhysicalFiles = request.getDeletePhysicalFiles();

log.info("Moving comics: targetDirectory={}", targetDirectory);
log.info(" : renamingRule={}", renamingRule);
this.libraryService.moveComics(deletePhysicalFiles, targetDirectory, renamingRule);

return new MoveComicsResponse(true);
}
}
@@ -0,0 +1,51 @@
/*
* 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.net;

import com.fasterxml.jackson.annotation.JsonProperty;

public class MoveComicsRequest {
@JsonProperty("deletePhysicalFiles")
private Boolean deletePhysicalFiles;

@JsonProperty("targetDirectory")
private String targetDirectory;

@JsonProperty("renamingRule")
private String renamingRule;

public MoveComicsRequest(
Boolean deletePhysicalFiles, String targetDirectory, String renamingRule) {
this.deletePhysicalFiles = deletePhysicalFiles;
this.targetDirectory = targetDirectory;
this.renamingRule = renamingRule;
}

public Boolean getDeletePhysicalFiles() {
return deletePhysicalFiles;
}

public String getTargetDirectory() {
return targetDirectory;
}

public String getRenamingRule() {
return renamingRule;
}
}
@@ -0,0 +1,34 @@
/*
* 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.net;

import com.fasterxml.jackson.annotation.JsonProperty;

public class MoveComicsResponse {
@JsonProperty("success")
private boolean success;

public MoveComicsResponse(boolean success) {
this.success = success;
}

public boolean isSuccess() {
return this.success;
}
}
Expand Up @@ -57,6 +57,8 @@ public class LibraryControllerTest {
private static final boolean TEST_RENAME_PAGES = RANDOM.nextBoolean();
private static final Boolean TEST_DELETE_PHYSICAL_FILES = RANDOM.nextBoolean();
private static final int TEST_CACHE_ENTRIES_CLEARED = RANDOM.nextInt();
private static final String TEST_RENAMING_RULE = "PUBLISHER/SERIES/VOLUME/SERIES vVOLUME #ISSUE";
private static final String TEST_DESTINATION_DIRECTORY = "/home/comixedreader/Documents/comics";

@InjectMocks private LibraryController libraryController;
@Mock private LibraryService libraryService;
Expand Down Expand Up @@ -255,4 +257,22 @@ public void testClearImageCacheWithError() throws LibraryException {

Mockito.verify(libraryService, Mockito.times(1)).clearImageCache();
}

@Test
public void testMoveLibrary() {
Mockito.doNothing()
.when(libraryService)
.moveComics(Mockito.anyBoolean(), Mockito.anyString(), Mockito.anyString());

MoveComicsResponse result =
libraryController.moveComics(
new MoveComicsRequest(
TEST_DELETE_PHYSICAL_FILES, TEST_DESTINATION_DIRECTORY, TEST_RENAMING_RULE));

assertNotNull(result);
assertTrue(result.isSuccess());

Mockito.verify(libraryService, Mockito.times(1))
.moveComics(TEST_DELETE_PHYSICAL_FILES, TEST_DESTINATION_DIRECTORY, TEST_RENAMING_RULE);
}
}
Expand Up @@ -36,6 +36,7 @@
import org.comixed.service.user.ComiXedUserException;
import org.comixed.service.user.UserService;
import org.comixed.task.model.ConvertComicsWorkerTask;
import org.comixed.task.model.MoveComicsWorkerTask;
import org.comixed.task.runner.Worker;
import org.comixed.utils.Utils;
import org.springframework.beans.factory.ObjectFactory;
Expand All @@ -56,6 +57,7 @@ public class LibraryService {
@Autowired private Worker worker;
@Autowired private Utils utils;
@Autowired private PageCacheService pageCacheService;
@Autowired private ObjectFactory<MoveComicsWorkerTask> moveComicsTaskObjectFactory;

public List<Comic> getComicsUpdatedSince(
String email, Date latestUpdatedDate, int maximumComics, long lastComicId) {
Expand Down Expand Up @@ -164,4 +166,22 @@ public void clearImageCache() throws LibraryException {
throw new LibraryException("failed to clean image cache directory", error);
}
}

/**
* Moves all comics in the library, renaming them using the specified naming rule.
*
* @param deletePhysicalFiles
* @param directory the root directory
* @param renamingRule the name rule
*/
public void moveComics(Boolean deletePhysicalFiles, String directory, String renamingRule) {
log.debug("Creating move comics task");
MoveComicsWorkerTask task = this.moveComicsTaskObjectFactory.getObject();
log.debug("Setting directory: {}", directory);
task.setDirectory(directory);
log.debug("Setting renaming rule: {}", renamingRule);
task.setRenamingRule(renamingRule);
log.debug("Enqueuing task");
this.worker.addTasksToQueue(task);
}
}
Expand Up @@ -21,6 +21,8 @@
import org.comixed.service.user.ComiXedUserException;
import org.comixed.service.user.UserService;
import org.comixed.task.model.ConvertComicsWorkerTask;
import org.comixed.task.model.MoveComicsWorkerTask;
import org.comixed.task.model.WorkerTask;
import org.comixed.task.runner.Worker;
import org.comixed.utils.Utils;
import org.junit.Before;
Expand All @@ -46,6 +48,9 @@ public class LibraryServiceTest {
private static final Long TEST_USER_ID = 723L;
private static final String TEST_IMAGE_CACHE_DIRECTORY =
"/home/ComiXedReader/.comixed/image-cache";
private static final String TEST_DIRECTORY = "/home/comixedreader/Documents/comics";
private static final String TEST_RENAMING_RULES =
"$PUBLISHER/$SERIES/$VOLUME/$SERIES [v$VOLUME] #$ISSUE $COVERDATE";

@InjectMocks private LibraryService libraryService;
@Mock private ComicRepository comicRepository;
Expand All @@ -64,6 +69,8 @@ public class LibraryServiceTest {
@Mock private ComiXedUser user;
@Mock private LastReadDatesRepository lastReadDatesRepository;
@Mock private PageCacheService pageCacheService;
@Mock private ObjectFactory<MoveComicsWorkerTask> moveComicsTaskObjectFactory;
@Mock private MoveComicsWorkerTask moveComicsWorkerTask;

private List<Comic> comicList = new ArrayList<>();
private Comic comic1 = new Comic();
Expand Down Expand Up @@ -233,4 +240,16 @@ public void testClearImageCacheError() throws LibraryException, IOException {
Mockito.verify(pageCacheService, Mockito.times(1)).getRootDirectory();
Mockito.verify(utils, Mockito.times(1)).deleteDirectoryContents(TEST_IMAGE_CACHE_DIRECTORY);
}

@Test
public void testMoveComics() {
Mockito.when(moveComicsTaskObjectFactory.getObject()).thenReturn(moveComicsWorkerTask);
Mockito.doNothing().when(worker).addTasksToQueue(Mockito.any(WorkerTask.class));

libraryService.moveComics(TEST_DELETE_PHYSICAL_FILES, TEST_DIRECTORY, TEST_RENAMING_RULES);

Mockito.verify(moveComicsWorkerTask, Mockito.times(1)).setDirectory(TEST_DIRECTORY);
Mockito.verify(moveComicsWorkerTask, Mockito.times(1)).setRenamingRule(TEST_RENAMING_RULES);
Mockito.verify(worker, Mockito.times(1)).addTasksToQueue(moveComicsWorkerTask);
}
}
@@ -0,0 +1,109 @@
/*
* 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.task.encoders;

import lombok.extern.log4j.Log4j2;
import org.comixed.model.comic.Comic;
import org.comixed.model.tasks.Task;
import org.comixed.model.tasks.TaskType;
import org.comixed.repositories.tasks.TaskRepository;
import org.comixed.task.model.MoveComicWorkerTask;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

/**
* <code>MoveComicTaskEncoder</code> encodes instances of {@link MoveComicWorkerTask}.
*
* @author Darryl L. Pierce
*/
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Log4j2
public class MoveComicTaskEncoder extends AbstractTaskEncoder<MoveComicWorkerTask> {
public static final String DIRECTORY = "destination-directory";
public static final String RENAMING_RULE = "renaming-rule";

@Autowired private ObjectFactory<MoveComicWorkerTask> moveComicWorkerTaskObjectFactory;
@Autowired private TaskRepository taskRepository;

private Comic comic;
private String directory;
private String renamingRule;

@Override
public Task encode() {
log.debug(
"encoding move comic task: id={} directory={} rename rule={}",
this.comic.getId(),
this.directory,
this.renamingRule);

Task result = new Task();
result.setTaskType(TaskType.MOVE_COMIC);
result.setComic(this.comic);
result.setProperty(DIRECTORY, this.directory);
result.setProperty(RENAMING_RULE, this.renamingRule);

return result;
}

@Override
public MoveComicWorkerTask decode(Task task) {
this.taskRepository.delete(task);

log.debug("Decoding move comic task: id={}", task.getId());

MoveComicWorkerTask result = this.moveComicWorkerTaskObjectFactory.getObject();
result.setComic(task.getComic());
result.setDirectory(task.getProperty(DIRECTORY));
result.setRenamingRule(task.getProperty(RENAMING_RULE));

return result;
}

/**
* Sets the comic to be moved.
*
* @param comic the comic
*/
public void setComic(Comic comic) {
this.comic = comic;
}

/**
* Sets the root directory into which the comic is to be moved.
*
* @param directory the directory
*/
public void setDirectory(String directory) {
this.directory = directory;
}

/**
* Sets the renaming rule to be applied.
*
* @param renamingRule the renaming rule
*/
public void setRenamingRule(String renamingRule) {
this.renamingRule = renamingRule;
}
}

0 comments on commit 0548c7b

Please sign in to comment.