Skip to content

Commit

Permalink
BXC-4719 archive project (#109)
Browse files Browse the repository at this point in the history
* archive project

* list projects archive status

* count number of archived projects

* projectPaths to projectNames, .archived directory constant, rename archiveProject to archiveProjects, throw error, remove countArchivedProjects method, add --include-archived option to listProjects cmd

* add description for --include-archived option

* move line out of loop, remove unused constant, rename method to listProjectsInDirectory, check for /.archived/

* if project archived, set allowedActions to empty array

* if project archived, set allowedActions to empty array

* throw error if no projectNames
  • Loading branch information
krwong authored Sep 25, 2024
1 parent 6a9d6e2 commit 24729bd
Show file tree
Hide file tree
Showing 9 changed files with 303 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package edu.unc.lib.boxc.migration.cdm;

import edu.unc.lib.boxc.migration.cdm.exceptions.InvalidProjectStateException;
import edu.unc.lib.boxc.migration.cdm.services.ArchiveProjectsService;
import org.slf4j.Logger;
import picocli.CommandLine.Option;
import picocli.CommandLine.ParentCommand;
import picocli.CommandLine.Command;

import java.util.List;
import java.util.concurrent.Callable;

import static edu.unc.lib.boxc.migration.cdm.util.CLIConstants.outputLogger;
import static org.slf4j.LoggerFactory.getLogger;

@Command(name = "archive",
description = {"Archive a project or list of projects. These projects will be moved to an 'archived' folder."})
public class ArchiveProjectsCommand implements Callable<Integer> {
private static final Logger log = getLogger(ArchiveProjectsCommand.class);

@ParentCommand
private CLIMain parentCommand;

@Option(names = { "-p", "--project-names" },
split = ",",
description = {"Specify project or list of projects to be archived"})
private List<String> projectNames;

private ArchiveProjectsService archiveProjectsService;

@Override
public Integer call() throws Exception {
try {
archiveProjectsService = new ArchiveProjectsService();
archiveProjectsService.archiveProjects(parentCommand.getWorkingDirectory(), projectNames);
return 0;
} catch(InvalidProjectStateException e) {
outputLogger.info("Archiving project(s) failed: {}", e.getMessage());
return 1;
} catch (Exception e) {
log.error("Failed to archive project(s)", e);
outputLogger.info("Archiving project(s) failed: {}", e.getMessage());
return 1;
}
}
}
3 changes: 2 additions & 1 deletion src/main/java/edu/unc/lib/boxc/migration/cdm/CLIMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
AggregateFilesCommand.class,
PermissionsCommand.class,
ExportObjectsCommand.class,
ListProjectsCommand.class
ListProjectsCommand.class,
ArchiveProjectsCommand.class
})
public class CLIMain implements Callable<Integer> {
@Option(names = { "-w", "--work-dir" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import edu.unc.lib.boxc.migration.cdm.services.ProjectPropertiesService;
import edu.unc.lib.boxc.migration.cdm.services.SourceFileService;
import org.slf4j.Logger;
import picocli.CommandLine.Option;
import picocli.CommandLine.Command;
import picocli.CommandLine.ParentCommand;

Expand All @@ -29,6 +30,10 @@ public class ListProjectsCommand implements Callable<Integer> {
@ParentCommand
private CLIMain parentCommand;

@Option(names = { "-ia", "--include-archived" },
description = "Include archived projects in list of chompb projects")
private boolean includeArchived;

private CdmFieldService fieldService;
private CdmIndexService indexService;
private ProjectPropertiesService propertiesService;
Expand All @@ -40,7 +45,7 @@ public Integer call() {
try {
initialize();
ObjectMapper mapper = new ObjectMapper();
JsonNode listProjects = listProjectsService.listProjects(parentCommand.getWorkingDirectory());
JsonNode listProjects = listProjectsService.listProjects(parentCommand.getWorkingDirectory(), includeArchived);
String prettyPrintList = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(listProjects);
outputLogger.info(prettyPrintList);
return 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package edu.unc.lib.boxc.migration.cdm.services;

import edu.unc.lib.boxc.migration.cdm.exceptions.InvalidProjectStateException;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

/**
* Service for archiving chompb project(s)
* @author krwong
*/
public class ArchiveProjectsService {
private static final Logger log = LoggerFactory.getLogger(ArchiveProjectsService.class);
public static final String ARCHIVED = ".archived";

/**
* Archive a list of projects
*/
public void archiveProjects(Path currentDirectory, List<String> projectNames) throws IOException {
if (projectNames == null || projectNames.isEmpty()) {
throw new InvalidProjectStateException("Project names cannot be empty. " +
"Please specify a valid project name(s).");
}

Path archiveDirectory = currentDirectory.resolve(ARCHIVED);
for (String projectName : projectNames) {
Path projectDirectory = currentDirectory.resolve(projectName);

if (Files.notExists(projectDirectory)) {
throw new InvalidProjectStateException("Migration project " + projectName + " does not exist");
}

if (Files.isDirectory(projectDirectory)) {
FileUtils.moveDirectoryToDirectory(projectDirectory.toFile(),
archiveDirectory.toFile(), true);
} else {
throw new InvalidProjectStateException("Migration project " + projectName + " is not a directory");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public class ListProjectsService {
* List projects in given directory
* @return jsonNode of projects
*/
public JsonNode listProjects(Path directory) throws Exception {
public JsonNode listProjects(Path directory, boolean includeArchived) throws Exception {
if (Files.notExists(directory)) {
throw new InvalidProjectStateException("Path " + directory + " does not exist");
}
Expand All @@ -55,14 +55,30 @@ public JsonNode listProjects(Path directory) throws Exception {
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
ArrayNode arrayNode = mapper.createArrayNode();

// list projects
listProjectsInDirectory(directory, mapper, arrayNode);

// list archived projects
if (includeArchived) {
listProjectsInDirectory(directory.resolve(ArchiveProjectsService.ARCHIVED), mapper, arrayNode);
}

log.debug(arrayNode.toString());
return arrayNode;
}

private void listProjectsInDirectory(Path directory, ObjectMapper mapper, ArrayNode arrayNode) throws Exception {
for (File file : directory.toFile().listFiles()) {
if (file.isDirectory()) {
try {
MigrationProject project = initializeProject(file.toPath());

Path projectPath = file.toPath().toAbsolutePath();
String projectStatus = status(project);
ArrayNode allowedActions = mapper.valueToTree(allowedActions(project));
ArrayNode allowedActions = mapper.createArrayNode();
if (!projectStatus.equals("archived")) {
allowedActions = mapper.valueToTree(allowedActions(project));
}
JsonNode projectProperties = mapper.readTree(project.getProjectPropertiesPath().toFile());

// add project info to JSON
Expand All @@ -77,9 +93,6 @@ public JsonNode listProjects(Path directory) throws Exception {
}
}
}
log.debug(arrayNode.toString());

return arrayNode;
}

/**
Expand All @@ -90,11 +103,9 @@ public JsonNode listProjects(Path directory) throws Exception {
private String status(MigrationProject project) {
String status = null;

// TODO: add archived state
// if () {
// status = "archived";
// }
if (!project.getProjectProperties().getSipsSubmitted().isEmpty()) {
if (project.getProjectPath().toString().contains("/" + ArchiveProjectsService.ARCHIVED + "/")) {
status = "archived";
} else if (!project.getProjectProperties().getSipsSubmitted().isEmpty()) {
status = "ingested";
} else if (project.getProjectProperties().getSipsGeneratedDate() != null) {
status = "sips_generated";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package edu.unc.lib.boxc.migration.cdm;

import edu.unc.lib.boxc.migration.cdm.services.ArchiveProjectsService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.nio.file.Files;

import static org.junit.jupiter.api.Assertions.assertTrue;

public class ArchiveProjectsCommandIT extends AbstractCommandIT {
private static final String PROJECT_NAME_2 = "proj2";

@BeforeEach
public void setup() throws Exception {
initProjectAndHelper();
setupChompbConfig();
}

@Test
public void archiveProjectTest() throws Exception {
String[] args = new String[] {
"-w", String.valueOf(baseDir),
"archive",
"-p", project.getProjectName()};
executeExpectSuccess(args);

assertTrue(Files.exists(tmpFolder.resolve(ArchiveProjectsService.ARCHIVED + "/"
+ project.getProjectName())));
}

@Test
public void archiveInvalidProjectTest() throws Exception {
String[] args = new String[] {
"-w", String.valueOf(baseDir),
"archive",
"-p", PROJECT_NAME_2};
executeExpectFailure(args);

assertOutputContains("Migration project " + PROJECT_NAME_2 + " does not exist");
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package edu.unc.lib.boxc.migration.cdm;

import edu.unc.lib.boxc.migration.cdm.model.MigrationProject;
import edu.unc.lib.boxc.migration.cdm.services.ArchiveProjectsService;
import edu.unc.lib.boxc.migration.cdm.services.ListProjectsService;
import edu.unc.lib.boxc.migration.cdm.services.MigrationProjectFactory;
import edu.unc.lib.boxc.migration.cdm.test.BxcEnvironmentHelper;
Expand All @@ -9,6 +10,7 @@
import org.junit.jupiter.api.Test;

import java.nio.file.Path;
import java.util.Collections;

public class ListProjectsCommandIT extends AbstractCommandIT {
private static final String PROJECT_ID_2 = "proj2";
Expand All @@ -30,7 +32,7 @@ public void invalidDirectoryTest() throws Exception {
}

@Test
public void listProjectsTest() throws Exception {
public void listProjectTest() throws Exception {
String[] args = new String[] {
"-w", String.valueOf(baseDir),
"list_projects" };
Expand All @@ -42,6 +44,24 @@ public void listProjectsTest() throws Exception {
assertOutputContains("\"name\" : \"" + PROJECT_ID + "\"");
}

@Test
public void listArchivedProjectTest() throws Exception {
ArchiveProjectsService archiveProjectsService = new ArchiveProjectsService();
archiveProjectsService.archiveProjects(tmpFolder, Collections.singletonList(PROJECT_ID));

String[] args = new String[] {
"-w", String.valueOf(baseDir),
"list_projects",
"--include-archived"};
executeExpectSuccess(args);

assertOutputContains("\"" + ListProjectsService.PROJECT_PATH + "\" : \""
+ baseDir.resolve(ArchiveProjectsService.ARCHIVED+ "/" + PROJECT_ID) + "\"");
assertOutputContains("\"" + ListProjectsService.STATUS + "\" : \"archived\"");
assertOutputContains("\"" + ListProjectsService.ALLOWED_ACTIONS + "\" : [ ]");
assertOutputContains("\"name\" : \"" + PROJECT_ID + "\"");
}

@Test
public void listMultipleProjectsTest() throws Exception {
project = MigrationProjectFactory.createMigrationProject(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package edu.unc.lib.boxc.migration.cdm.services;

import edu.unc.lib.boxc.migration.cdm.model.MigrationProject;
import edu.unc.lib.boxc.migration.cdm.test.BxcEnvironmentHelper;
import edu.unc.lib.boxc.migration.cdm.test.CdmEnvironmentHelper;
import edu.unc.lib.boxc.migration.cdm.test.SipServiceHelper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.MockitoAnnotations.openMocks;

public class ArchiveProjectsServiceTest {
private static final String PROJECT_NAME = "proj";
private static final String PROJECT_NAME_2 = "proj2";

@TempDir
public Path tmpFolder;

private SipServiceHelper testHelper;
private MigrationProject project;
private ArchiveProjectsService service;

private AutoCloseable closeable;

@BeforeEach
public void setup() throws Exception {
closeable = openMocks(this);
project = MigrationProjectFactory.createMigrationProject(
tmpFolder, PROJECT_NAME, null, "user", CdmEnvironmentHelper.DEFAULT_ENV_ID,
BxcEnvironmentHelper.DEFAULT_ENV_ID, MigrationProject.PROJECT_SOURCE_CDM);
testHelper = new SipServiceHelper(project, tmpFolder);

service = new ArchiveProjectsService();
}

@AfterEach
void closeService() throws Exception {
closeable.close();
}

@Test
public void archiveProjectTest() throws Exception {
List<String> testProjects = new ArrayList<>();
testProjects.add(PROJECT_NAME);
service.archiveProjects(tmpFolder, testProjects);

assertTrue(Files.exists(tmpFolder.resolve(service.ARCHIVED + "/" + PROJECT_NAME)));
}

@Test
public void archiveInvalidProjectTest() throws Exception {
try {
List<String> testProjects = new ArrayList<>();
testProjects.add(PROJECT_NAME_2);
service.archiveProjects(tmpFolder, testProjects);
fail();
} catch (Exception e) {
assertTrue(e.getMessage().contains("Migration project " + PROJECT_NAME_2 + " does not exist"));
}
}

@Test
public void archiveEmptyProjectTest() throws Exception {
try {
List<String> testProjects = new ArrayList<>();;
service.archiveProjects(tmpFolder, testProjects);
fail();
} catch (Exception e) {
assertTrue(e.getMessage().contains("Project names cannot be empty"));
}
}

@Test
public void archiveMultipleProjectsTest() throws Exception {
project = MigrationProjectFactory.createMigrationProject(
tmpFolder, PROJECT_NAME_2, null, "user", CdmEnvironmentHelper.DEFAULT_ENV_ID,
BxcEnvironmentHelper.DEFAULT_ENV_ID, MigrationProject.PROJECT_SOURCE_CDM);

List<String> testProjects = new ArrayList<>();
testProjects.add(PROJECT_NAME);
testProjects.add(PROJECT_NAME_2);
service.archiveProjects(tmpFolder, testProjects);

assertTrue(Files.exists(tmpFolder.resolve(service.ARCHIVED + "/" + PROJECT_NAME)));
assertTrue(Files.exists(tmpFolder.resolve(service.ARCHIVED + "/" + PROJECT_NAME_2)));
}
}
Loading

0 comments on commit 24729bd

Please sign in to comment.