Skip to content

Commit

Permalink
BXC-4412 basic set command (#84)
Browse files Browse the repository at this point in the history
* add set command and tests, add id option

* remove unused code

* remove previousStatePermissions instance variable, add getPermissions helper method, small fixes

* fix query, use prepared stmt, add tests for setting grouped work

* move prepared statement to doesIdExistInIndex helper method, revert getIds

* add groupa:group11 in grouped_gilmer, fix tests

* remove unused code
  • Loading branch information
krwong authored Feb 29, 2024
1 parent b8ab759 commit 65aa414
Show file tree
Hide file tree
Showing 11 changed files with 347 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,25 @@ public int generate(@Mixin PermissionMappingOptions options) throws Exception {
}
}

@Command(name = "set",
description = "Add or update an entry in an existing permissions mapping file")
public int set(@Mixin PermissionMappingOptions options) throws Exception {
try {
initialize();

permissionsService.setPermissions(options);
outputLogger.info("Permissions mapping generated for cdmId {}", options.getCdmId());
return 0;
} catch (MigrationException | IllegalArgumentException e) {
outputLogger.info("Cannot set mappings: {}", e.getMessage());
return 1;
} catch (Exception e) {
log.error("Failed to set mappings", e);
outputLogger.info("Failed to set mappings: {}", e.getMessage(), e);
return 1;
}
}

@Command(name = "validate",
description = "Validate the permissions mapping file for this project")
public int validate() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ public class PermissionMappingOptions {
description = "Add entry for every file (compound children and grouped children) to CSV mapping")
private boolean withFiles;

@Option(names = {"-id", "--cdm-id"},
description = "Work or File to add or update, can be cdmId or group work identifier")
private String cdmId;

@Option(names = {"-e", "--everyone"},
description = "The patron access role assigned to the “everyone” group.")
private UserRole everyone;
Expand Down Expand Up @@ -53,6 +57,14 @@ public boolean isWithFiles() {
return withFiles;
}

public String getCdmId() {
return cdmId;
}

public void setCdmId(String cdmId) {
this.cdmId = cdmId;
}

public void setWithFiles(boolean withFiles) {
this.withFiles = withFiles;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package edu.unc.lib.boxc.migration.cdm.services;

import edu.unc.lib.boxc.auth.api.UserRole;
import edu.unc.lib.boxc.migration.cdm.exceptions.InvalidProjectStateException;
import edu.unc.lib.boxc.migration.cdm.exceptions.MigrationException;
import edu.unc.lib.boxc.migration.cdm.exceptions.StateAlreadyExistsException;
import edu.unc.lib.boxc.migration.cdm.model.CdmFieldInfo;
Expand All @@ -25,10 +26,11 @@
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static edu.unc.lib.boxc.migration.cdm.services.CdmIndexService.ENTRY_TYPE_COMPOUND_CHILD;
import static edu.unc.lib.boxc.migration.cdm.services.CdmIndexService.ENTRY_TYPE_FIELD;
import static org.slf4j.LoggerFactory.getLogger;

/**
Expand All @@ -47,7 +49,7 @@ public class PermissionsService {
* @param options permission mapping options
*/
public void generatePermissions(PermissionMappingOptions options) throws Exception {
Path fieldsPath = project.getPermissionsPath();
Path permissionsPath = project.getPermissionsPath();
ensureMappingState(options.isForce());

String everyoneField;
Expand All @@ -64,7 +66,7 @@ public void generatePermissions(PermissionMappingOptions options) throws Excepti
}

try (
BufferedWriter writer = Files.newBufferedWriter(fieldsPath);
BufferedWriter writer = Files.newBufferedWriter(permissionsPath);
CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT.withHeader(PermissionsInfo.CSV_HEADERS));
) {
if (options.isWithDefault()) {
Expand All @@ -84,6 +86,33 @@ public void generatePermissions(PermissionMappingOptions options) throws Excepti
ProjectPropertiesSerialization.write(project);
}

/**
* Update existing permission mapping csv
* @param options permission mapping options
*/
public void setPermissions(PermissionMappingOptions options) throws Exception {
if (!doesIdExistInIndex(options.getCdmId())) {
throw new IllegalArgumentException("Id " + options.getCdmId() + " does not exist in this project.");
}

Path permissionsMappingPath = project.getPermissionsPath();
if (!Files.exists(permissionsMappingPath)) {
throw new InvalidProjectStateException("Permissions csv does not exist.");
}

// add or update permission for a specific cdmId
List<List<String>> records = updateCsvRecords(options);

try (
BufferedWriter writer = Files.newBufferedWriter(permissionsMappingPath);
CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT.withHeader(PermissionsInfo.CSV_HEADERS));
) {
for (List<String> record : records) {
csvPrinter.printRecord(record.get(0), record.get(1), record.get(2));
}
}
}

/**
* @param project
* @return the permissions mapping info for the provided project
Expand Down Expand Up @@ -166,8 +195,8 @@ private List<String> getIds(String query) {

getIndexService();
try (Connection conn = indexService.openDbConnection()) {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query);
var stmt = conn.createStatement();
var rs = stmt.executeQuery(query);
while (rs.next()) {
if (!rs.getString(1).isEmpty()) {
ids.add(rs.getString(1));
Expand Down Expand Up @@ -216,6 +245,68 @@ private List<String> queryForMappedIds(PermissionMappingOptions options) {
return mappedIds;
}

private boolean doesIdExistInIndex(String id) {
String query = "select " + CdmFieldInfo.CDM_ID + " from " + CdmIndexService.TB_NAME
+ " where " + CdmFieldInfo.CDM_ID + " = ?";

getIndexService();
try (Connection conn = indexService.openDbConnection()) {
var stmt = conn.prepareStatement(query);
stmt.setString(1, id);
var rs = stmt.executeQuery();
while (rs.next()) {
if (!rs.getString(1).isEmpty()) {
return true;
}
}
return false;
} catch (SQLException e) {
throw new MigrationException("Error interacting with export index", e);
}
}

private List<List<String>> updateCsvRecords(PermissionMappingOptions options) {
List<CSVRecord> previousRecords = getPermissions();
List<List<String>> updatedRecords = new ArrayList<>();
Set<String> cdmIds = new HashSet<>();
String everyoneField = getAssignedRoleValue(options.isStaffOnly(), options.getEveryone());
String authenticatedField = getAssignedRoleValue(options.isStaffOnly(), options.getAuthenticated());

// update existing entry
for (CSVRecord record : previousRecords) {
cdmIds.add(record.get(0));
if (record.get(0).equals(options.getCdmId())) {
updatedRecords.add(Arrays.asList(record.get(0), everyoneField, authenticatedField));
} else {
updatedRecords.add(Arrays.asList(record.get(0), record.get(1), record.get(2)));
}
}

// add new entry
if (!cdmIds.contains(options.getCdmId())) {
updatedRecords.add(Arrays.asList(options.getCdmId(), everyoneField, authenticatedField));
}

return updatedRecords;
}

public List<CSVRecord> getPermissions() {
List<CSVRecord> permissions = new ArrayList<>();
try (
Reader reader = Files.newBufferedReader(project.getPermissionsPath());
CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT
.withFirstRecordAsHeader()
.withHeader(PermissionsInfo.CSV_HEADERS)
.withTrim());
) {
permissions = csvParser.getRecords();
} catch (IOException e) {
log.error("Failed to list permissions", e);
}

return permissions;
}

private CdmIndexService getIndexService() {
if (indexService == null) {
indexService = new CdmIndexService();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,12 +449,14 @@ private void assertDefaultMapping(String expectedDest, String expectedColl) thro

private void assertArchivalCollectionMapping(String defaultDest, String defaultColl) throws Exception {
var mappings = getMappings();
assertMappingCount(mappings, 3);
DestinationMapping group2Mapping = mappings.get(0);
assertMappingCount(mappings, 4);
DestinationMapping group11Mapping = mappings.get(0);
assertEquals("groupa:group11", group11Mapping.getId());
DestinationMapping group2Mapping = mappings.get(1);
assertEquals("groupa:group2", group2Mapping.getId());
DestinationMapping group1Mapping = mappings.get(1);
DestinationMapping group1Mapping = mappings.get(2);
assertEquals("groupa:group1", group1Mapping.getId());
DestinationMapping defaultMapping = mappings.get(2);
DestinationMapping defaultMapping = mappings.get(3);
assertEquals(DestinationsInfo.DEFAULT_ID, defaultMapping.getId());
assertEquals(defaultDest, defaultMapping.getDestination());
assertEquals(defaultColl, defaultMapping.getCollectionId());
Expand Down Expand Up @@ -492,5 +494,10 @@ private void archivalCollectionSolrResponse() throws Exception {
.willReturn(aResponse()
.withBodyFile("arc_coll_resp_group2.bin")
.withHeader("Content-Type", "application/octet-stream")));
//groupa:group11
stubFor(get(urlEqualTo("/solr/select?q=collectionId%3Agroup11&fq=resourceType%3ACollection&wt=javabin&version=2"))
.willReturn(aResponse()
.withBodyFile("arc_coll_resp_group2.bin")
.withHeader("Content-Type", "application/octet-stream")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ public void filterMultipleExcludeTest() throws Exception {
"-e", ""};
executeExpectSuccess(args);

assertOutputContains("Filtering index from 5 to 1 remaining entries");
assertOutputContains("Filtering index from 5 to 2 remaining entries");
assertOutputContains("Filtering of index for my_proj completed");
assertRemaining(1);
assertRemaining(2);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package edu.unc.lib.boxc.migration.cdm;

import edu.unc.lib.boxc.migration.cdm.model.PermissionsInfo;
import edu.unc.lib.boxc.migration.cdm.options.GroupMappingOptions;
import edu.unc.lib.boxc.migration.cdm.options.GroupMappingSyncOptions;
import edu.unc.lib.boxc.migration.cdm.services.PermissionsService;
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -189,6 +191,68 @@ public void generateWorkAndFilePermissionsWithDefault() throws Exception {
assertMapping(3, "27", "canViewMetadata", "canViewMetadata");
}

@Test
public void setPermissionExistingEntry() throws Exception {
FileUtils.write(project.getPermissionsPath().toFile(),
"25,canViewOriginals,canViewOriginals", StandardCharsets.UTF_8, true);

testHelper.indexExportData("mini_gilmer");
String[] args = new String[] {
"-w", project.getProjectPath().toString(),
"permissions", "set",
"-id", "25",
"-e", "canViewMetadata",
"-a", "canViewMetadata"};
executeExpectSuccess(args);
assertMapping(0, "25", "canViewMetadata", "canViewMetadata");
}

@Test
public void setPermissionNewEntry() throws Exception {
String[] args = new String[] {
"-w", project.getProjectPath().toString(),
"permissions", "generate",
"-wd",
"--everyone", "canViewOriginals",
"--authenticated", "canViewOriginals"};
executeExpectSuccess(args);

testHelper.indexExportData("mini_gilmer");
String[] args2 = new String[] {
"-w", project.getProjectPath().toString(),
"permissions", "set",
"-id", "26",
"-e", "canViewMetadata",
"-a", "canViewMetadata"};
executeExpectSuccess(args2);
assertMapping(0, "default", "canViewOriginals", "canViewOriginals");
assertMapping(1, "26", "canViewMetadata", "canViewMetadata");
}

@Test
public void setPermissionNewGroupedWorkEntry() throws Exception {
String[] args = new String[] {
"-w", project.getProjectPath().toString(),
"permissions", "generate",
"-wd",
"--everyone", "canViewOriginals",
"--authenticated", "canViewOriginals"};
executeExpectSuccess(args);

testHelper.indexExportData("grouped_gilmer");
setupGroupedIndex();
String[] args2 = new String[] {
"-w", project.getProjectPath().toString(),
"permissions", "set",
"-id", "grp:groupa:group1",
"-e", "canViewMetadata",
"-a", "canViewMetadata"};
executeExpectSuccess(args2);
assertMapping(0, "default", "canViewOriginals", "canViewOriginals");
assertMapping(1, "grp:groupa:group1", "canViewMetadata", "canViewMetadata");
assertMappingCount(2);
}

@Test
public void validateValidDefaultPermissions() throws Exception {
String[] args = new String[] {
Expand Down Expand Up @@ -232,6 +296,28 @@ public void validateInvalidDefaultPermissions() throws Exception {
assertEquals(2, output.split(" - ").length, "Must only be two errors: " + output);
}

@Test
public void validateValidSetPermissions() throws Exception {
FileUtils.write(project.getPermissionsPath().toFile(),
"25,canViewOriginals,canViewOriginals", StandardCharsets.UTF_8, true);

testHelper.indexExportData("mini_gilmer");
String[] args = new String[] {
"-w", project.getProjectPath().toString(),
"permissions", "set",
"-id", "25",
"--everyone", "canViewMetadata",
"--authenticated", "canViewMetadata"};
executeExpectSuccess(args);

String[] args2 = new String[] {
"-w", project.getProjectPath().toString(),
"permissions", "validate" };
executeExpectSuccess(args2);

assertOutputContains("PASS: Permissions mapping at path " + project.getPermissionsPath() + " is valid");
}

private void assertMapping(int index, String id, String expectedEveryone, String expectedAuthenticated)
throws IOException {
var mappings = getMappings();
Expand All @@ -246,7 +332,17 @@ private List<PermissionsInfo.PermissionMapping> getMappings() throws IOException
return info.getMappings();
}

private void assertMappingCount(List<PermissionsInfo.PermissionMapping> mappings, int count) {
private void assertMappingCount(int count) throws IOException {
var mappings = getMappings();
assertEquals(count, mappings.size());
}

private void setupGroupedIndex() throws Exception {
var options = new GroupMappingOptions();
options.setGroupField("groupa");
testHelper.getGroupMappingService().generateMapping(options);
var syncOptions = new GroupMappingSyncOptions();
syncOptions.setSortField("file");
testHelper.getGroupMappingService().syncMappings(syncOptions);
}
}
Loading

0 comments on commit 65aa414

Please sign in to comment.