From e11dc54294b7eccccf69e180c12e9228b480e1ef Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Fri, 12 Jul 2024 09:40:10 -0500 Subject: [PATCH 01/41] Fix Test failure regarding EmailRecipientContactTest file. The error: ``` [ERROR] Tests run: 5, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0 s <<< FAILURE! - in org.tdl.vireo.model.EmailRecipientContactTest [ERROR] testGetEmails Time elapsed: 0 s <<< FAILURE! org.opentest4j.AssertionFailedError: Emails array does not have the correct length. ==> expected: <0> but was: <2> at org.tdl.vireo.model.EmailRecipientContactTest.testGetEmails(EmailRecipientContactTest.java:50) ``` This PR and commit: - https://github.com/TexasDigitalLibrary/Vireo/pull/1923 - https://github.com/TexasDigitalLibrary/Vireo/commit/7b4a7b8c1ab66df89f42f40148291b4a88011ee7 Did not also update the unit test. This updates the unit test to now mock `getFieldValuesByPredicateValue()` instead of mocking `getFieldValuesByPredicate()`. --- .../java/org/tdl/vireo/model/EmailRecipientContactTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/tdl/vireo/model/EmailRecipientContactTest.java b/src/test/java/org/tdl/vireo/model/EmailRecipientContactTest.java index bc49a0043..5ec32e2d3 100644 --- a/src/test/java/org/tdl/vireo/model/EmailRecipientContactTest.java +++ b/src/test/java/org/tdl/vireo/model/EmailRecipientContactTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import java.util.ArrayList; @@ -40,10 +41,11 @@ public void testGetEmails() { fieldValues.add(fieldValue); fieldValues.add(otherFieldValue); fieldPredicate.setId(1L); + fieldPredicate.setValue("fp_value"); ReflectionTestUtils.setField(emailRecipientContact, "fieldPredicate", fieldPredicate); - when(submission.getFieldValuesByPredicate(any(FieldPredicate.class))).thenReturn(fieldValues); + when(submission.getFieldValuesByPredicateValue(anyString())).thenReturn(fieldValues); List got = emailRecipientContact.getEmails(submission); From ce7785ac2e8b9da70bf9c7e2c1aaf582c50b5357 Mon Sep 17 00:00:00 2001 From: William Welling Date: Fri, 12 Jul 2024 11:06:45 -0500 Subject: [PATCH 02/41] Sort action log by date to obtain most recent action --- .../submission/adminSubmissionViewController.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/app/controllers/submission/adminSubmissionViewController.js b/src/main/webapp/app/controllers/submission/adminSubmissionViewController.js index 0e9cd011d..ca08b6e76 100644 --- a/src/main/webapp/app/controllers/submission/adminSubmissionViewController.js +++ b/src/main/webapp/app/controllers/submission/adminSubmissionViewController.js @@ -1,4 +1,4 @@ -vireo.controller("AdminSubmissionViewController", function ($anchorScroll, $controller, $location, $routeParams, $scope, $timeout, DepositLocationRepo, EmailRecipient, EmailRecipientType, EmailTemplateRepo, EmbargoRepo, FieldPredicateRepo, FieldValue, FileUploadService, SidebarService, SubmissionRepo, SubmissionStatuses, SubmissionStatusRepo, UserRepo, UserService, UserSettings, WsApi) { +vireo.controller("AdminSubmissionViewController", function ($anchorScroll, $controller, $filter, $location, $routeParams, $scope, $timeout, DepositLocationRepo, EmailRecipient, EmailRecipientType, EmailTemplateRepo, EmbargoRepo, FieldPredicateRepo, FieldValue, FileUploadService, SidebarService, SubmissionRepo, SubmissionStatuses, SubmissionStatusRepo, UserRepo, UserService, UserSettings, WsApi) { angular.extend(this, $controller('AbstractController', { $scope: $scope @@ -590,14 +590,16 @@ vireo.controller("AdminSubmissionViewController", function ($anchorScroll, $cont "hasPrimaryDocument": $scope.hasPrimaryDocument }; + var getLastActionLog = function() { + return $filter('orderBy')($scope.submission.actionLogs, 'actionDate', true)[0]; + } + var getLastActionDate = function() { - var index = $scope.submission.actionLogs.length - 1; - return $scope.submission.actionLogs[index].actionDate; + return getLastActionLog().actionDate; }; var getLastActionEntry = function() { - var index = $scope.submission.actionLogs.length - 1; - return $scope.submission.actionLogs[index].entry; + return getLastActionLog().entry; }; var isUmiRelease = function() { From 2022e5fceb17ce40bbd2912c2f2ca7b4c9bc4be3 Mon Sep 17 00:00:00 2001 From: William Welling Date: Fri, 12 Jul 2024 12:44:28 -0500 Subject: [PATCH 03/41] Fix new field value controller --- .../java/org/tdl/vireo/controller/FieldValueController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/tdl/vireo/controller/FieldValueController.java b/src/main/java/org/tdl/vireo/controller/FieldValueController.java index 44b4cbdcc..a7b85dcdf 100644 --- a/src/main/java/org/tdl/vireo/controller/FieldValueController.java +++ b/src/main/java/org/tdl/vireo/controller/FieldValueController.java @@ -34,7 +34,7 @@ public class FieldValueController { @GetMapping("/predicate/{value}") @PreAuthorize("hasRole('STUDENT')") public ApiResponse getFieldValuesByPredicateValue(@PathVariable String value) { - return new ApiResponse(SUCCESS, fieldValueRepo.getAllValuesByFieldPredicateValue("submission_type")); + return new ApiResponse(SUCCESS, fieldValueRepo.getAllValuesByFieldPredicateValue(value)); } } From 86178a1fb31bc3dee21f4922a847709c2a60e485 Mon Sep 17 00:00:00 2001 From: William Welling Date: Fri, 12 Jul 2024 12:45:16 -0500 Subject: [PATCH 04/41] Update submission list filter by graduation semester --- .../submission/submissionListController.js | 9 ++++++ .../furtherFilterByDegreeDate.html | 28 ++++++------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/main/webapp/app/controllers/submission/submissionListController.js b/src/main/webapp/app/controllers/submission/submissionListController.js index 06fc7008f..f692b6afd 100644 --- a/src/main/webapp/app/controllers/submission/submissionListController.js +++ b/src/main/webapp/app/controllers/submission/submissionListController.js @@ -203,6 +203,10 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle update(true); + const withoutActiveFilter = function(value) { + return $scope.activeFilters.namedSearchFilters.filter((nsf) => nsf.filterValues.indexOf(value) >= 0).length == 0; + }; + var assignableUsers = UserRepo.getAssignableUsers(0, 0); var savedFilters = SavedFilterRepo.getAll(); var emailTemplates = EmailTemplateRepo.getAll(); @@ -217,6 +221,7 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle var packagers = PackagerRepo.getAll(); var controlledVocabularies = ControlledVocabularyRepo.getAll(); var submissionTypeList = FieldValueRepo.findValuesByPredicateValue('submission_type'); + var graduationSemesters = FieldValueRepo.findValuesByPredicateValue('dc.date.issued'); var addBatchCommentEmail = function (message) { batchCommentEmail.adding = true; @@ -625,6 +630,8 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle "embargos": embargos, "submissionTypeList": submissionTypeList, "assignableUsers": assignableUsers, + "graduationSemesters": graduationSemesters, + "withoutActiveFilter": withoutActiveFilter, "defaultLimit": 3, "getTypeAheadByPredicateName": getTypeAheadByPredicateName, "datepickerOptions": datepickerOptions @@ -646,6 +653,8 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle "submissionStatuses": submissionStatuses, "newStatus": submissionStatuses[0], "assignableUsers": assignableUsers, + "graduationSemesters": graduationSemesters, + "withoutActiveFilter": withoutActiveFilter, "batchAssignTo": batchAssignTo, "batchPublish": batchPublish, "resetBatchCommentEmailModal": resetBatchCommentEmailModal, diff --git a/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByDegreeDate.html b/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByDegreeDate.html index 4b8acb005..bfed4b9f9 100644 --- a/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByDegreeDate.html +++ b/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByDegreeDate.html @@ -1,20 +1,10 @@ -
-
- - - - -
- + +
From 02f296917fcf734c7a4441d65ac7de4749e9213c Mon Sep 17 00:00:00 2001 From: William Welling Date: Mon, 15 Jul 2024 09:27:30 -0500 Subject: [PATCH 05/41] Make title unique on submission list column Submission list column title and predicate are derived without referential integrity from organization workflow field profile gloss and field profile field predicate value. --- .../tdl/vireo/model/SubmissionListColumn.java | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/tdl/vireo/model/SubmissionListColumn.java b/src/main/java/org/tdl/vireo/model/SubmissionListColumn.java index a2814f8d7..7eaf1125a 100644 --- a/src/main/java/org/tdl/vireo/model/SubmissionListColumn.java +++ b/src/main/java/org/tdl/vireo/model/SubmissionListColumn.java @@ -2,36 +2,31 @@ import static javax.persistence.FetchType.EAGER; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.ManyToOne; import javax.persistence.OrderColumn; -import javax.persistence.Table; import javax.persistence.Transient; -import javax.persistence.UniqueConstraint; -import org.tdl.vireo.model.validation.SubmissionListColumnValidator; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import com.fasterxml.jackson.annotation.JsonIgnore; +import org.tdl.vireo.model.validation.SubmissionListColumnValidator; import edu.tamu.weaver.validation.model.ValidatingBaseEntity; @Entity -@Table(uniqueConstraints = @UniqueConstraint(columnNames = { "title", "predicate", "input_type_id" })) public class SubmissionListColumn extends ValidatingBaseEntity { @ManyToOne(fetch = EAGER, optional = false) private InputType inputType; - // NOTE: should this be unique? Are there any front end requirements for it to be unique. - @Column(nullable = false) + @Column(nullable = false, unique = true) private String title; @Column(nullable = true) From 3b6ca23f3fb7cb5b83324864ef4045ea9404ee7e Mon Sep 17 00:00:00 2001 From: William Welling Date: Mon, 15 Jul 2024 13:33:33 -0500 Subject: [PATCH 06/41] Add Student Name submission list column with combined predicate --- .../SYSTEM_Default_Submission_List_Columns.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json b/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json index b81bf84e0..130cb7ea2 100644 --- a/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json +++ b/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json @@ -221,5 +221,14 @@ "inputType": { "name": "INPUT_TEXT" } + }, + { + "title": "Student Name", + "sort": "ASC", + "predicate": "last_name, first_name middle_name", + "status": null, + "inputType": { + "name": "INPUT_TEXT" + } } ] From 0bb5566f35e26962ff6f958095fe614dc6db0e41 Mon Sep 17 00:00:00 2001 From: William Welling Date: Mon, 15 Jul 2024 13:34:01 -0500 Subject: [PATCH 07/41] Update the submission query to afford sorting on combined predicate --- .../model/repo/impl/SubmissionRepoImpl.java | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java index ec4894c22..90c432117 100644 --- a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java +++ b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java @@ -5,9 +5,8 @@ import static edu.tamu.weaver.response.ApiAction.UPDATE; import static edu.tamu.weaver.response.ApiStatus.SUCCESS; -import edu.tamu.weaver.auth.model.Credentials; -import edu.tamu.weaver.data.model.repo.impl.AbstractWeaverRepoImpl; -import edu.tamu.weaver.response.ApiResponse; +import javax.sql.DataSource; + import java.io.IOException; import java.sql.ResultSet; import java.sql.SQLException; @@ -24,7 +23,7 @@ import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.regex.Pattern; -import javax.sql.DataSource; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -65,6 +64,10 @@ import org.tdl.vireo.model.repo.custom.SubmissionRepoCustom; import org.tdl.vireo.service.AssetService; +import edu.tamu.weaver.auth.model.Credentials; +import edu.tamu.weaver.data.model.repo.impl.AbstractWeaverRepoImpl; +import edu.tamu.weaver.response.ApiResponse; + public class SubmissionRepoImpl extends AbstractWeaverRepoImpl implements SubmissionRepoCustom { final static Logger logger = LoggerFactory.getLogger(SubmissionRepoImpl.class); @@ -455,7 +458,8 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { int n = 0; int totalFieldValueConditions = 0; - for (SubmissionListColumn submissionListColumn : allSubmissionListColumns) { + for (int i = 0; i < allSubmissionListColumns.size(); i++) { + SubmissionListColumn submissionListColumn = allSubmissionListColumns.get(i); if (submissionListColumn.getSortOrder() > 0 || submissionListColumn.getFilters().size() > 0 || allColumnSearchFilters.size() > 0 || submissionListColumn.getVisible()) { if (sqlColumnsBuilders.containsKey(submissionListColumn.getId())) { @@ -467,7 +471,31 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { switch (String.join(".", submissionListColumn.getValuePath())) { case "fieldValues.value": - Long predicateId = fieldPredicateRepo.findByValue(submissionListColumn.getPredicate()).getId(); + FieldPredicate fieldPredicate = fieldPredicateRepo.findByValue(submissionListColumn.getPredicate()); + + // if the predicate is not found, see if it is space delimited set of predicates + // for each predicate, add a submission list column clone with predicate and sort order updated + if (fieldPredicate == null) { + String[] predicates = submissionListColumn.getPredicate().split(" "); + int sortOrder = submissionListColumn.getSortOrder(); + for(int j = 0; j < predicates.length; ++j) { + + SubmissionListColumn column = new SubmissionListColumn( + submissionListColumn.getTitle(), + submissionListColumn.getSort(), + predicates[j].replace(",", "").trim(), + submissionListColumn.getInputType() + ); + + column.setSortOrder(sortOrder++); + + allSubmissionListColumns.add(i + j + 1, column); + } + + continue; + } + + Long predicateId = fieldPredicate.getId(); // @formatter:off if (submissionListColumn.getSortOrder() > 0 || submissionListColumn.getFilters().size() > 0) { @@ -1093,8 +1121,7 @@ public void setColumnOrdering(Sort sort, List sqlAliasBuilders, StringBu if (!sqlAliasBuilders.contains(value)) { sqlAliasBuilders.add(value); } - - sqlOrderBysBuilder.append(" ").append(value).append(" ").append(sort.name()).append(","); + sqlOrderBysBuilder.append("\n ").append(value).append(" ").append(sort.name()).append(","); } } From 5c769a68f539a04acf72b2b386fbc5af30651ea2 Mon Sep 17 00:00:00 2001 From: William Welling Date: Mon, 15 Jul 2024 13:34:43 -0500 Subject: [PATCH 08/41] Update submission list to render column for combined predicates --- .../submission/submissionListController.js | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/src/main/webapp/app/controllers/submission/submissionListController.js b/src/main/webapp/app/controllers/submission/submissionListController.js index 06fc7008f..91a8a32a4 100644 --- a/src/main/webapp/app/controllers/submission/submissionListController.js +++ b/src/main/webapp/app/controllers/submission/submissionListController.js @@ -189,10 +189,7 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle $scope.columns = angular.fromJson(angular.toJson($filter('orderBy')($filter('exclude')(submissionListColumns, $scope.excludedColumns, 'title'), 'title'))); - angular.extend(filterColumns, { - userFilterColumns: managerFilterColumns, - inactiveFilterColumns: $filter('orderBy')($filter('exclude')(submissionListColumnsForManage, managerFilterColumns, 'title'), 'title') - }); + setFilterColumns(managerFilterColumns, $filter('orderBy')($filter('exclude')(submissionListColumnsForManage, managerFilterColumns, 'title'), 'title')); if (reloadList === true) { query(); @@ -351,6 +348,14 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle }); }; + var setFilterColumns = function(userFilterColumns, inactiveFilterColumns) { + // exclude columns which have multiple predicates + angular.extend(filterColumns, { + userFilterColumns: userFilterColumns.filter(c => (c.predicate === undefined || c.predicate == null) || c.predicate.trim().indexOf(' ') === -1), + inactiveFilterColumns: inactiveFilterColumns.filter(c => (c.predicate === undefined || c.predicate == null) || c.predicate.trim().indexOf(' ') === -1) + }); + } + $scope.addRowFilter = function ($index, row) { // When removing the last row for a page, and the page number is the last if ($scope.page.content.length == 1 && $scope.page.number > 0 && $scope.page.number == $scope.page.totalPages) { @@ -539,21 +544,36 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle var getValueFromArray = function (array, col) { var value = ""; - for (var j in array) { - var member = array[j]; - if (member.fieldPredicate !== undefined) { - if (member.fieldPredicate.value === col.predicate) { - value += value.length > 0 ? ", " + member.value : member.value; - } - } else { - var path = col.valuePath; - var curr = member; - for (var p = 1; p < path.length; p++) { - curr = curr[path[p]]; + var predicates = col.predicate.split(' '); + var pv; + for (var v of predicates) { + var predicate = v.replace(',', '').trim(); + + var delimeter = ', '; + for (var j in array) { + var member = array[j]; + if (member.fieldPredicate !== undefined) { + if (member.fieldPredicate.value === predicate) { + + // if set of predicates, determine delimeter by precense of trailing comma + if (predicates.length > 1) { + delimeter = (!!pv && pv.endsWith(',')) ? ', ' : ' '; + } + + value += value.length > 0 ? delimeter + member.value : member.value; + } + } else { + var path = col.valuePath; + var curr = member; + for (var p = 1; p < path.length; p++) { + curr = curr[path[p]]; + } + value += value.length > 0 ? ', ' + curr : curr; } - value += value.length > 0 ? ", " + curr : curr; } + pv = v; } + return value; }; @@ -780,10 +800,7 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle var userFilterColumns = filtersPreviouslyDisplayed.concat(filterColumns.userFilterColumns); var inactiveFilterColumns = filtersPreviouslyDisabled.concat(filterColumns.inactiveFilterColumns); - angular.extend(filterColumns, { - userFilterColumns: userFilterColumns, - inactiveFilterColumns: inactiveFilterColumns - }); + setFilterColumns(userFilterColumns, inactiveFilterColumns); updateChange(false); }; From 5016d6fcf6376780cf162c0b580aa7399db7088f Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 16 Jul 2024 13:45:41 -0500 Subject: [PATCH 09/41] Sort the graduation semesters --- .../controllers/submission/submissionListController.js | 10 +++++++++- src/main/webapp/app/repo/fieldValueRepo.js | 6 +++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/app/controllers/submission/submissionListController.js b/src/main/webapp/app/controllers/submission/submissionListController.js index f692b6afd..0cfac6c3d 100644 --- a/src/main/webapp/app/controllers/submission/submissionListController.js +++ b/src/main/webapp/app/controllers/submission/submissionListController.js @@ -221,7 +221,15 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle var packagers = PackagerRepo.getAll(); var controlledVocabularies = ControlledVocabularyRepo.getAll(); var submissionTypeList = FieldValueRepo.findValuesByPredicateValue('submission_type'); - var graduationSemesters = FieldValueRepo.findValuesByPredicateValue('dc.date.issued'); + var graduationSemesters = FieldValueRepo.findValuesByPredicateValue('dc.date.issued', function (a, b) { + const [monthA, yearA] = a.split(' '); + const [monthB, yearB] = b.split(' '); + + const dateA = new Date(`${monthA} 1, ${yearA}`); + const dateB = new Date(`${monthB} 1, ${yearB}`); + + return dateB - dateA; + }); var addBatchCommentEmail = function (message) { batchCommentEmail.adding = true; diff --git a/src/main/webapp/app/repo/fieldValueRepo.js b/src/main/webapp/app/repo/fieldValueRepo.js index cecb28546..54a673e35 100644 --- a/src/main/webapp/app/repo/fieldValueRepo.js +++ b/src/main/webapp/app/repo/fieldValueRepo.js @@ -2,7 +2,7 @@ vireo.repo("FieldValueRepo", function FieldValueRepo(FieldValue, WsApi) { var fieldValueRepo = this; - fieldValueRepo.findValuesByPredicateValue = function(value) { + fieldValueRepo.findValuesByPredicateValue = function(value, sort) { var fieldValues = []; // FieldValue exists on the API Mapping but is under the Submission controller, which is not correct and so manually define the entire mapping inline here. @@ -20,6 +20,10 @@ vireo.repo("FieldValueRepo", function FieldValueRepo(FieldValue, WsApi) { for (var i in values) { fieldValues.push(values[i]); } + + if (sort) { + fieldValues = fieldValues.sort(sort); + } } } }); From 132485118653bee4aea26e96d63b2d7311d7960c Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 17 Jul 2024 08:12:30 -0500 Subject: [PATCH 10/41] Update system data loader test --- .../java/org/tdl/vireo/service/SystemDataLoaderTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/tdl/vireo/service/SystemDataLoaderTest.java b/src/test/java/org/tdl/vireo/service/SystemDataLoaderTest.java index 815d03671..38a3c0c04 100644 --- a/src/test/java/org/tdl/vireo/service/SystemDataLoaderTest.java +++ b/src/test/java/org/tdl/vireo/service/SystemDataLoaderTest.java @@ -274,7 +274,7 @@ private void assertWorkflowStep(boolean isReload) { ? "Incorrect number of workflowStep found after reload" : "Incorrect number of workflowStep found"); - + assertWorkflowStepHeritableProperties(new int[] { 3, 18 }, "Personal Information", isReload); assertWorkflowStepHeritableProperties(new int[] { 0, 2 }, "License Agreement", isReload); assertWorkflowStepHeritableProperties(new int[] { 4, 13 }, "Document Information", isReload); @@ -480,19 +480,19 @@ private void assertSubmissionStatusState(SubmissionState expected, SubmissionSta private void assertSubmissionStatusTransitions(String[] expected, SubmissionStatus actual, boolean isReload) { assertEquals(expected.length, actual.getTransitionSubmissionStatuses().size(), - isReload + isReload ? String.format("%s submission status has incorrect number of transition status after reload", actual.getName()) : String.format("%s submission status has incorrect number of transition status", actual.getName())); for (String name : expected) { assertTrue(actual.getTransitionSubmissionStatuses().stream().anyMatch(ts -> ts.getName().equals(name)), - isReload + isReload ? String.format("%s submission status is missing %s transition status after reload", actual.getName(), name) : String.format("%s submission status is missing %s transition status", actual.getName(), name)); } } private void assertSubmissionListColumn(boolean isReload) { - assertEquals(61, submissionListColumnRepo.count(), + assertEquals(62, submissionListColumnRepo.count(), isReload ? "Incorrect number of submissionListColumn found after reload" : "Incorrect number of submissionListColumn found"); From ca2581d970b32ae1219a2a27a11c4fd207e09075 Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 17 Jul 2024 08:33:57 -0500 Subject: [PATCH 11/41] Fix organization management controller tests --- .../settings/organizationManagementControllerTest.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/webapp/tests/unit/controllers/settings/organizationManagementControllerTest.js b/src/main/webapp/tests/unit/controllers/settings/organizationManagementControllerTest.js index 06d112f34..6891a2955 100644 --- a/src/main/webapp/tests/unit/controllers/settings/organizationManagementControllerTest.js +++ b/src/main/webapp/tests/unit/controllers/settings/organizationManagementControllerTest.js @@ -244,6 +244,7 @@ describe("controller: OrganizationManagementController", function () { spyOn(AccordionService, "closeAll"); spyOn(OrganizationRepo, "reorderWorkflowSteps"); + scope.selectedOrganization = mockOrganization(q); scope.reorderWorkflowStepDown(1); expect(AccordionService.closeAll).toHaveBeenCalled(); @@ -253,6 +254,7 @@ describe("controller: OrganizationManagementController", function () { spyOn(AccordionService, "closeAll"); spyOn(OrganizationRepo, "reorderWorkflowSteps"); + scope.selectedOrganization = mockOrganization(q); scope.reorderWorkflowStepUp(1); expect(AccordionService.closeAll).toHaveBeenCalled(); From 3961eee99d3d88ff249ccbc72de1e26360a3e67c Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 17 Jul 2024 14:25:17 -0500 Subject: [PATCH 12/41] Make user netid unique Co-Authored-By: Q'Sean <134101252+qtamu@users.noreply.github.com> --- src/main/java/org/tdl/vireo/model/User.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/tdl/vireo/model/User.java b/src/main/java/org/tdl/vireo/model/User.java index 87a9d9fc4..7917b8e0e 100644 --- a/src/main/java/org/tdl/vireo/model/User.java +++ b/src/main/java/org/tdl/vireo/model/User.java @@ -55,7 +55,7 @@ public class User extends AbstractWeaverUserDetails { private static final long serialVersionUID = -614285536644750464L; @JsonView(Views.Partial.class) - @Column(nullable = true) + @Column(nullable = true, unique = true) private String netid; @JsonView(Views.SubmissionList.class) From 20145c82628d6b31bbd820b73b5608fe65a8b4a3 Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 17 Jul 2024 14:25:59 -0500 Subject: [PATCH 13/41] Add find by netid user repository method Co-Authored-By: Q'Sean <134101252+qtamu@users.noreply.github.com> --- src/main/java/org/tdl/vireo/model/repo/UserRepo.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/tdl/vireo/model/repo/UserRepo.java b/src/main/java/org/tdl/vireo/model/repo/UserRepo.java index 0e5d859dc..2a1554460 100644 --- a/src/main/java/org/tdl/vireo/model/repo/UserRepo.java +++ b/src/main/java/org/tdl/vireo/model/repo/UserRepo.java @@ -17,6 +17,8 @@ public interface UserRepo extends AbstractWeaverUserRepo, UserRepoCustom { public User findByEmail(String email); + public User findByNetid(String netid); + public List findAllByRoleIn(List role, Sort sort); public List findAllByRoleIn(List role, Pageable pageable); From f586e46ad55bdb78dad84f3967baf3951e8552bc Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 17 Jul 2024 14:26:17 -0500 Subject: [PATCH 14/41] Optionally use netid as user unique identifier Co-Authored-By: Q'Sean <134101252+qtamu@users.noreply.github.com> --- .../auth/service/VireoUserCredentialsService.java | 12 ++++++++++-- src/main/resources/application.yml | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java b/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java index ff63920ef..9e128297d 100644 --- a/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java +++ b/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java @@ -4,6 +4,7 @@ import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.tdl.vireo.config.constant.ConfigurationName; import org.tdl.vireo.model.Role; @@ -20,9 +21,14 @@ public class VireoUserCredentialsService extends UserCredentialsService shibSettings = new HashMap(); Map shibValues = new HashMap(); @@ -63,7 +69,8 @@ public synchronized User updateUserByCredentials(Credentials credentials) { user = userRepo.create(credentials.getEmail(), credentials.getFirstName(), credentials.getLastName(), role); - user.setNetid(credentials.getAllCredentials().get(shibValues.get(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_NETID))); + user.setNetid(credentials.getNetid()); + if (credentials.getAllCredentials().get(shibValues.get(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_BIRTH_YEAR)) != null) { user.setBirthYear(Integer.parseInt(credentials.getAllCredentials().get(shibValues.get(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_BIRTH_YEAR)))); } @@ -90,6 +97,7 @@ public synchronized User updateUserByCredentials(Credentials credentials) { } credentials.setRole(user.getRole().toString()); + // TODO: is this correct? check for use of credentials uin credentials.setUin(user.getUsername()); return user; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 08a6541d2..aa9f835c0 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -177,6 +177,9 @@ app: dataLoader: initialize: true + # org.tdl.vireo.auth.service.VireoUserCredentialsService + useNetIdAsIdentifier: false + # edu.tamu.weaver.token.service.TokenService auth: security.jwt: From 1375dbf3f7eaaab1392753800518dd41941ab25e Mon Sep 17 00:00:00 2001 From: William Welling Date: Thu, 18 Jul 2024 12:41:16 -0500 Subject: [PATCH 15/41] Update credentials service to respect shibboleth configurations --- .../service/VireoUserCredentialsService.java | 131 +++++++++++------- src/main/resources/application.yml | 2 +- 2 files changed, 80 insertions(+), 53 deletions(-) diff --git a/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java b/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java index 9e128297d..11e45ec69 100644 --- a/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java +++ b/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java @@ -3,10 +3,10 @@ import java.util.HashMap; import java.util.Map; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import org.tdl.vireo.config.constant.ConfigurationName; import org.tdl.vireo.model.Role; import org.tdl.vireo.model.User; import org.tdl.vireo.model.repo.ConfigurationRepo; @@ -18,87 +18,114 @@ @Service public class VireoUserCredentialsService extends UserCredentialsService { + private static final String NETID = "APPLICATION_AUTH_SHIB_ATTRIBUTE_NETID"; + private static final String EMAIL = "APPLICATION_AUTH_SHIB_ATTRIBUTE_EMAIL"; + private static final String BIRTH_YEAR = "APPLICATION_AUTH_SHIB_ATTRIBUTE_BIRTH_YEAR"; + private static final String MIDDLE_NAME = "APPLICATION_AUTH_SHIB_ATTRIBUTE_MIDDLE_NAME"; + private static final String ORCID = "APPLICATION_AUTH_SHIB_ATTRIBUTE_ORCID"; + private static final String INSTITUTIONAL_IDENTIFIER = "APPLICATION_AUTH_SHIB_ATTRIBUTE_INSTITUTIONAL_IDENTIFIER"; + private static final String SHIBBOLETH = "shibboleth"; + + private static final Map shibSettings = new HashMap<>(); + + static { + shibSettings.put(NETID, "netid"); + shibSettings.put(EMAIL, "email"); + shibSettings.put(BIRTH_YEAR, "birthYear"); + shibSettings.put(MIDDLE_NAME, "middleName"); + shibSettings.put(ORCID, "orcid"); + shibSettings.put(INSTITUTIONAL_IDENTIFIER, "uin"); + } + @Autowired private ConfigurationRepo configurationRepo; - @Value("${app.useNetIdAsIdentifier:false}") - private boolean useNetIdAsIdentifier; + @Value("${app.useNetidAsIdentifier:false}") + private boolean useNetidAsIdentifier; @Override public synchronized User updateUserByCredentials(Credentials credentials) { - User user = useNetIdAsIdentifier - ? userRepo.findByNetid(credentials.getNetid()) - : userRepo.findByEmail(credentials.getEmail()); - - Map shibSettings = new HashMap(); - Map shibValues = new HashMap(); - - shibSettings.put(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_NETID, "netid"); - shibSettings.put(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_BIRTH_YEAR, "birthYear"); - shibSettings.put(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_MIDDLE_NAME, "middleName"); - shibSettings.put(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_ORCID, "orcid"); - shibSettings.put(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_INSTITUTIONAL_IDENTIFIER, "uin"); - - shibSettings.forEach((k, v) -> { - shibValues.put(k, configurationRepo.getValueByNameAndType(k, "shibboleth") != null ? configurationRepo.getValueByNameAndType(k, "shibboleth") : v); - }); - - String uin = credentials.getAllCredentials().get(shibValues.get(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_INSTITUTIONAL_IDENTIFIER)); - if (uin == null) { - uin = credentials.getEmail(); - } + Map shibValues = new HashMap<>(); - // TODO: check to see if credentials is from basic login or shibboleth - // do not create new user from basic login credentials that have no user! - if (user == null) { + shibSettings.forEach((k, v) -> + shibValues.put(k, configurationRepo.getValueByNameAndType(k, SHIBBOLETH) != null ? configurationRepo.getValueByNameAndType(k, SHIBBOLETH) : v) + ); - Role role = Role.ROLE_STUDENT; + String shibNetid = credentials.getAllCredentials().get(shibValues.get(NETID)); + String shibEmail = credentials.getAllCredentials().get(shibValues.get(EMAIL)); + String shibMiddleName = credentials.getAllCredentials().get(shibValues.get(MIDDLE_NAME)); + String shibOrcid = credentials.getAllCredentials().get(shibValues.get(ORCID)); - if (credentials.getRole() == null) { - credentials.setRole(role.toString()); - } + User user = useNetidAsIdentifier + ? userRepo.findByNetid(shibNetid) + : userRepo.findByEmail(shibEmail); - String shibEmail = credentials.getEmail(); + if (user == null) { + Role role = Role.ROLE_STUDENT; for (String email : admins) { if (email.equals(shibEmail)) { role = Role.ROLE_ADMIN; - credentials.setRole(role.toString()); } } - user = userRepo.create(credentials.getEmail(), credentials.getFirstName(), credentials.getLastName(), role); + user = userRepo.create(shibEmail, credentials.getFirstName(), credentials.getLastName(), role); - user.setNetid(credentials.getNetid()); + user.setNetid(shibNetid); + user.setMiddleName(shibMiddleName); + user.setOrcid(shibOrcid); - if (credentials.getAllCredentials().get(shibValues.get(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_BIRTH_YEAR)) != null) { - user.setBirthYear(Integer.parseInt(credentials.getAllCredentials().get(shibValues.get(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_BIRTH_YEAR)))); + if (credentials.getAllCredentials().containsKey(shibValues.get(BIRTH_YEAR))) { + String shibBirthYearValue = credentials.getAllCredentials().get(shibValues.get(BIRTH_YEAR)); + if (StringUtils.isNotEmpty(shibBirthYearValue)) { + user.setBirthYear(Integer.parseInt(shibBirthYearValue)); + } } - user.setMiddleName(credentials.getAllCredentials().get(shibValues.get(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_MIDDLE_NAME))); - user.setOrcid(credentials.getAllCredentials().get(shibValues.get(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_ORCID))); - user.setUsername(credentials.getEmail()); user = userRepo.save(user); } else { + boolean isUserUpdated = false; + + if (StringUtils.isNotEmpty(shibNetid) && !user.getNetid().equals(shibNetid)) { + user.setNetid(shibNetid); + isUserUpdated = true; + } - // TODO: update only if user properties does not match current credentials + if (credentials.getAllCredentials().containsKey(shibValues.get(BIRTH_YEAR))) { + String shibBirthYearValue = credentials.getAllCredentials().get(shibValues.get(BIRTH_YEAR)); -//DONT OVERWRITE WITH NULL VALUE -//MORE WORK NEEDED - //user.setNetid(credentials.getAllCredentials().get(shibValues.get(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_INSTITUTIONAL_IDENTIFIER))); - if (credentials.getAllCredentials().get(shibValues.get(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_BIRTH_YEAR)) != null) { - user.setBirthYear(Integer.parseInt(credentials.getAllCredentials().get(shibValues.get(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_BIRTH_YEAR)))); + if (StringUtils.isNotEmpty(shibBirthYearValue)) { + int shibBirthYear = Integer.parseInt(shibBirthYearValue); + if (shibBirthYear != user.getBirthYear()) { + user.setBirthYear(shibBirthYear); + isUserUpdated = true; + } + } } - user.setMiddleName(credentials.getAllCredentials().get(shibValues.get(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_MIDDLE_NAME))); - user.setOrcid(credentials.getAllCredentials().get(shibValues.get(ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_ORCID))); - user.setUsername(credentials.getEmail()); - user = userRepo.save(user); + if (StringUtils.isNotEmpty(shibMiddleName) && !user.getMiddleName().equals(shibMiddleName)) { + user.setMiddleName(shibMiddleName); + isUserUpdated = true; + } + + if (StringUtils.isNotEmpty(shibOrcid) && !user.getOrcid().equals(shibOrcid)) { + user.setOrcid(shibOrcid); + isUserUpdated = true; + } + + if (StringUtils.isNotEmpty(shibEmail) && !user.getUsername().equals(shibEmail)) { + user.setUsername(shibEmail); + isUserUpdated = true; + } + + if (isUserUpdated) { + user = userRepo.save(user); + } } credentials.setRole(user.getRole().toString()); - // TODO: is this correct? check for use of credentials uin - credentials.setUin(user.getUsername()); + + credentials.setNetid(user.getNetid()); return user; } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index aa9f835c0..796d9b793 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -178,7 +178,7 @@ app: initialize: true # org.tdl.vireo.auth.service.VireoUserCredentialsService - useNetIdAsIdentifier: false + useNetidAsIdentifier: false # edu.tamu.weaver.token.service.TokenService auth: From f654a0172bfa983f571ce334b07c732bbc9244b3 Mon Sep 17 00:00:00 2001 From: William Welling Date: Thu, 18 Jul 2024 14:27:19 -0500 Subject: [PATCH 16/41] Update shibboleth mapping configurations --- .../service/VireoUserCredentialsService.java | 40 +- .../resources/settings/SYSTEM_Defaults.json | 46 +- .../workflow/shibbolethAttributes.html | 500 +++++++++--------- 3 files changed, 306 insertions(+), 280 deletions(-) diff --git a/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java b/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java index 11e45ec69..908d8d843 100644 --- a/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java +++ b/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java @@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.tdl.vireo.config.constant.ConfigurationName; import org.tdl.vireo.model.Role; import org.tdl.vireo.model.User; import org.tdl.vireo.model.repo.ConfigurationRepo; @@ -18,12 +19,15 @@ @Service public class VireoUserCredentialsService extends UserCredentialsService { - private static final String NETID = "APPLICATION_AUTH_SHIB_ATTRIBUTE_NETID"; - private static final String EMAIL = "APPLICATION_AUTH_SHIB_ATTRIBUTE_EMAIL"; - private static final String BIRTH_YEAR = "APPLICATION_AUTH_SHIB_ATTRIBUTE_BIRTH_YEAR"; - private static final String MIDDLE_NAME = "APPLICATION_AUTH_SHIB_ATTRIBUTE_MIDDLE_NAME"; - private static final String ORCID = "APPLICATION_AUTH_SHIB_ATTRIBUTE_ORCID"; - private static final String INSTITUTIONAL_IDENTIFIER = "APPLICATION_AUTH_SHIB_ATTRIBUTE_INSTITUTIONAL_IDENTIFIER"; + private static final String NETID = ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_NETID; + private static final String EMAIL = ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_EMAIL; + private static final String BIRTH_YEAR = ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_BIRTH_YEAR; + private static final String FIRST_NAME = ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_FIRST_NAME; + private static final String MIDDLE_NAME = ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_MIDDLE_NAME; + private static final String LAST_NAME = ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_LAST_NAME; + private static final String ORCID = ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_ORCID; + private static final String INSTITUTIONAL_IDENTIFIER = ConfigurationName.APPLICATION_AUTH_SHIB_ATTRIBUTE_INSTITUTIONAL_IDENTIFIER; + private static final String SHIBBOLETH = "shibboleth"; private static final Map shibSettings = new HashMap<>(); @@ -32,7 +36,9 @@ public class VireoUserCredentialsService extends UserCredentialsService -
-
- - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - -
-
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
From a92542d4ae413c9354088f53e2b7fe9714c46ddf Mon Sep 17 00:00:00 2001 From: William Welling Date: Thu, 18 Jul 2024 14:34:40 -0500 Subject: [PATCH 17/41] Update system organization field profiles with mapped shib attribute --- .../organization/SYSTEM_Organization_Definition.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/organization/SYSTEM_Organization_Definition.json b/src/main/resources/organization/SYSTEM_Organization_Definition.json index 184717b65..01fa5f62f 100644 --- a/src/main/resources/organization/SYSTEM_Organization_Definition.json +++ b/src/main/resources/organization/SYSTEM_Organization_Definition.json @@ -113,7 +113,7 @@ }, "mappedShibAttribute": { "isSystemRequired": true, - "name": "APPLICATION_AUTH_SHIB_ATTRIBUTE_EMAIL", + "name": "auth.shib.attribute.email", "type": "shibboleth", "value": "email" }, @@ -160,7 +160,7 @@ }, "mappedShibAttribute": { "isSystemRequired": true, - "name": "APPLICATION_AUTH_SHIB_ATTRIBUTE_INSTITUTIONAL_IDENTIFIER", + "name": "auth.shib.attribute.institutionalIdentifier", "type": "shibboleth", "value": "uin" }, From fab5b9b1348b160d1f86f88ecdfce6f3b6ffcff6 Mon Sep 17 00:00:00 2001 From: William Welling Date: Thu, 18 Jul 2024 14:36:04 -0500 Subject: [PATCH 18/41] Remove system out println --- .../tdl/vireo/auth/service/VireoUserCredentialsService.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java b/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java index 908d8d843..b8150ee7b 100644 --- a/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java +++ b/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java @@ -57,12 +57,6 @@ public synchronized User updateUserByCredentials(Credentials credentials) { shibValues.put(k, configurationRepo.getValueByNameAndType(k, SHIBBOLETH) != null ? configurationRepo.getValueByNameAndType(k, SHIBBOLETH) : v) ); - System.out.println("\n\nshib settings map: " + shibSettings + "\n\n"); - - System.out.println("\n\nshib values map: " + shibValues + "\n\n"); - - System.out.println("\n\nall credentials: " + credentials.getAllCredentials() + "\n\n"); - String shibNetid = credentials.getAllCredentials().get(shibValues.get(NETID)); String shibEmail = credentials.getAllCredentials().get(shibValues.get(EMAIL)); String shibFirstName = credentials.getAllCredentials().get(shibValues.get(FIRST_NAME)); From caa1ccd5e59029a122bc222d586fdebfcbee3fce Mon Sep 17 00:00:00 2001 From: William Welling Date: Fri, 19 Jul 2024 09:47:55 -0500 Subject: [PATCH 19/41] Fix field profile repo tests --- .../vireo/model/repo/AbstractRepoTest.java | 2 ++ .../model/repo/FieldProfileRepoTest.java | 20 +++++++++---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/test/java/org/tdl/vireo/model/repo/AbstractRepoTest.java b/src/test/java/org/tdl/vireo/model/repo/AbstractRepoTest.java index 80c272910..184e8c5c9 100644 --- a/src/test/java/org/tdl/vireo/model/repo/AbstractRepoTest.java +++ b/src/test/java/org/tdl/vireo/model/repo/AbstractRepoTest.java @@ -133,6 +133,8 @@ public abstract class AbstractRepoTest { // WorkFlow test protected static final String TEST_WORKFLOW_NAME = "Test Workflow"; protected static final String TEST_GLOSS = "Test Gloss"; + protected static final String TEST_GLOSS_2 = "Test Gloss 2"; + protected static final String TEST_GLOSS_3 = "Test Gloss 3"; protected static final String TEST_SEVERABLE_GLOSS = "Test Severable Gloss"; // Organization Category test diff --git a/src/test/java/org/tdl/vireo/model/repo/FieldProfileRepoTest.java b/src/test/java/org/tdl/vireo/model/repo/FieldProfileRepoTest.java index 21627b1ff..446d9ca1d 100644 --- a/src/test/java/org/tdl/vireo/model/repo/FieldProfileRepoTest.java +++ b/src/test/java/org/tdl/vireo/model/repo/FieldProfileRepoTest.java @@ -307,11 +307,11 @@ public void testInheritAndRemoveFieldProfiles() { workflowStep = workflowStepRepo.findById(workflowStep.getId()).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); - FieldProfile fp2 = fieldProfileRepo.create(workflowStep, fieldPredicate2, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); + FieldProfile fp2 = fieldProfileRepo.create(workflowStep, fieldPredicate2, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS_2, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); workflowStep = workflowStepRepo.findById(workflowStep.getId()).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); - FieldProfile fp3 = fieldProfileRepo.create(workflowStep, fieldPredicate3, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); + FieldProfile fp3 = fieldProfileRepo.create(workflowStep, fieldPredicate3, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS_3, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); workflowStep = workflowStepRepo.findById(workflowStep.getId()).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); @@ -458,11 +458,11 @@ public void testReorderAggregateFieldProfiles() throws WorkflowStepNonOverrideab workflowStep = workflowStepRepo.findById(workflowStep.getId()).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); - FieldProfile fp2 = fieldProfileRepo.create(workflowStep, fieldPredicate2, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); + FieldProfile fp2 = fieldProfileRepo.create(workflowStep, fieldPredicate2, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS_2, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); workflowStep = workflowStepRepo.findById(workflowStep.getId()).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); - FieldProfile fp3 = fieldProfileRepo.create(workflowStep, fieldPredicate3, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); + FieldProfile fp3 = fieldProfileRepo.create(workflowStep, fieldPredicate3, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS_3, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); workflowStep = workflowStepRepo.findById(workflowStep.getId()).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); @@ -761,11 +761,11 @@ public void testMaintainFieldOrderWhenOverriding() throws HeritableModelNonOverr workflowStep = workflowStepRepo.findById(workflowStep.getId()).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); - FieldProfile fp2 = fieldProfileRepo.create(workflowStep, fieldPredicate2, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); + FieldProfile fp2 = fieldProfileRepo.create(workflowStep, fieldPredicate2, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS_2, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); workflowStep = workflowStepRepo.findById(workflowStep.getId()).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); - fieldProfileRepo.create(workflowStep, fieldPredicate3, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); + fieldProfileRepo.create(workflowStep, fieldPredicate3, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS_3, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); workflowStep = workflowStepRepo.findById(workflowStep.getId()).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); @@ -822,11 +822,11 @@ public void testMakeFieldProfileNonOverrideable() throws HeritableModelNonOverri workflowStep = workflowStepRepo.findById(wsId).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); - FieldProfile fp2 = fieldProfileRepo.create(workflowStep, fieldPredicate2, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); + FieldProfile fp2 = fieldProfileRepo.create(workflowStep, fieldPredicate2, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS_2, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); workflowStep = workflowStepRepo.findById(wsId).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); - /* FieldProfile fp3 = */ fieldProfileRepo.create(workflowStep, fieldPredicate3, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); + /* FieldProfile fp3 = */ fieldProfileRepo.create(workflowStep, fieldPredicate3, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS_3, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); workflowStep = workflowStepRepo.findById(wsId).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); @@ -928,11 +928,11 @@ public void testDeleteFPAtDescendantOrgAndDuplicateWSIsDeletedToo() throws Herit workflowStep = workflowStepRepo.findById(workflowStep.getId()).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); - /* FieldProfile fp2 = */ fieldProfileRepo.create(workflowStep, fieldPredicate2, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); + /* FieldProfile fp2 = */ fieldProfileRepo.create(workflowStep, fieldPredicate2, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS_2, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); workflowStep = workflowStepRepo.findById(workflowStep.getId()).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); - /* FieldProfile fp3 = */ fieldProfileRepo.create(workflowStep, fieldPredicate3, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); + /* FieldProfile fp3 = */ fieldProfileRepo.create(workflowStep, fieldPredicate3, inputType, TEST_FIELD_PROFILE_USAGE, TEST_GLOSS_3, TEST_FIELD_PROFILE_REPEATABLE, TEST_FIELD_PROFILE_OVERRIDEABLE, TEST_FIELD_PROFILE_ENABLED, TEST_FIELD_PROFILE_OPTIONAL, TEST_FIELD_PROFILE_FLAGGED, TEST_FIELD_PROFILE_LOGGED, TEST_FIELD_PROFILE_DEFAULT_VALUE); workflowStep = workflowStepRepo.findById(workflowStep.getId()).get(); fieldPredicate = fieldPredicateRepo.findById(fieldPredicate.getId()).get(); From 2491915b9164ab9ea77ac823e32afb20068cb793 Mon Sep 17 00:00:00 2001 From: William Welling Date: Mon, 22 Jul 2024 12:51:47 -0500 Subject: [PATCH 20/41] Add exception handler for file not found --- .../advice/CustomResponseEntityExceptionHandler.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/org/tdl/vireo/controller/advice/CustomResponseEntityExceptionHandler.java b/src/main/java/org/tdl/vireo/controller/advice/CustomResponseEntityExceptionHandler.java index 6a2c04b6f..f8c1e3edb 100644 --- a/src/main/java/org/tdl/vireo/controller/advice/CustomResponseEntityExceptionHandler.java +++ b/src/main/java/org/tdl/vireo/controller/advice/CustomResponseEntityExceptionHandler.java @@ -5,6 +5,8 @@ import javax.persistence.EntityNotFoundException; +import java.nio.file.NoSuchFileException; + import org.hibernate.exception.ConstraintViolationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,6 +74,15 @@ public ApiResponse handleEntityNotFoundException(EntityNotFoundException excepti return new ApiResponse(ERROR, exception.getMessage()); } + @ExceptionHandler(NoSuchFileException.class) + @ResponseStatus(value = HttpStatus.NOT_FOUND) + @ResponseBody + public ApiResponse handleNoSuchFileExceptionn(NoSuchFileException exception) { + logger.error(exception.getMessage()); + logger.debug(exception.getMessage(), exception); + return new ApiResponse(ERROR, exception.getMessage()); + } + @ExceptionHandler(MultipartException.class) @ResponseStatus(value = HttpStatus.PAYLOAD_TOO_LARGE) @ResponseBody From c2d377a2a102b662c27b3f95a46f64d81b7ff21e Mon Sep 17 00:00:00 2001 From: William Welling Date: Mon, 22 Jul 2024 12:52:20 -0500 Subject: [PATCH 21/41] Ignore hibernate contact info properties --- src/main/java/org/tdl/vireo/model/ContactInfo.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/tdl/vireo/model/ContactInfo.java b/src/main/java/org/tdl/vireo/model/ContactInfo.java index 08c66fa9c..d9eda905e 100644 --- a/src/main/java/org/tdl/vireo/model/ContactInfo.java +++ b/src/main/java/org/tdl/vireo/model/ContactInfo.java @@ -10,11 +10,13 @@ import javax.persistence.Entity; import javax.persistence.OneToOne; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.tdl.vireo.model.validation.ContactInfoValidator; import edu.tamu.weaver.validation.model.ValidatingBaseEntity; @Entity +@JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" }) public class ContactInfo extends ValidatingBaseEntity { @OneToOne(cascade = { DETACH, MERGE, REFRESH, REMOVE }, fetch = EAGER, optional = true, orphanRemoval = true) From db0d5b271639bbc89811cece74eb15a02db3f153 Mon Sep 17 00:00:00 2001 From: William Welling Date: Mon, 22 Jul 2024 12:58:14 -0500 Subject: [PATCH 22/41] Afford sorting and filtering by last action of submission --- .../java/org/tdl/vireo/model/ActionLog.java | 4 +- .../java/org/tdl/vireo/model/Submission.java | 70 ++++---- .../tdl/vireo/model/repo/SubmissionRepo.java | 2 +- .../model/repo/impl/ActionLogRepoImpl.java | 168 ++++++++++-------- .../model/repo/impl/SubmissionRepoImpl.java | 95 +++++++--- ...YSTEM_Default_Submission_List_Columns.json | 10 +- 6 files changed, 215 insertions(+), 134 deletions(-) diff --git a/src/main/java/org/tdl/vireo/model/ActionLog.java b/src/main/java/org/tdl/vireo/model/ActionLog.java index 61bd698d5..6b401e4f7 100644 --- a/src/main/java/org/tdl/vireo/model/ActionLog.java +++ b/src/main/java/org/tdl/vireo/model/ActionLog.java @@ -30,12 +30,12 @@ public class ActionLog extends ValidatingBaseEntity { @ManyToOne(optional = true) private User user; - @JsonView(Views.SubmissionIndividual.class) + @JsonView(Views.SubmissionList.class) @Column(nullable = true) @Temporal(TemporalType.TIMESTAMP) private Calendar actionDate; - @JsonView(Views.SubmissionIndividual.class) + @JsonView(Views.SubmissionList.class) @Column(nullable = false, columnDefinition = "text") private String entry; diff --git a/src/main/java/org/tdl/vireo/model/Submission.java b/src/main/java/org/tdl/vireo/model/Submission.java index 6ce9d024b..182ac1643 100644 --- a/src/main/java/org/tdl/vireo/model/Submission.java +++ b/src/main/java/org/tdl/vireo/model/Submission.java @@ -3,15 +3,6 @@ import static javax.persistence.CascadeType.ALL; import static javax.persistence.FetchType.LAZY; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; - import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.Entity; @@ -24,20 +15,28 @@ import javax.persistence.NamedEntityGraphs; import javax.persistence.NamedSubgraph; import javax.persistence.OneToMany; +import javax.persistence.OneToOne; import javax.persistence.OrderColumn; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.UniqueConstraint; -import org.hibernate.annotations.Fetch; -import org.hibernate.annotations.FetchMode; -import org.tdl.vireo.model.response.Views; -import org.tdl.vireo.model.validation.SubmissionValidator; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonView; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; +import org.tdl.vireo.model.response.Views; +import org.tdl.vireo.model.validation.SubmissionValidator; import edu.tamu.weaver.validation.model.ValidatingBaseEntity; @@ -54,6 +53,7 @@ name = "graph.Submission.List", attributeNodes = { @NamedAttributeNode(value = "assignee", subgraph = "subgraph.user"), + @NamedAttributeNode(value = "lastAction", subgraph = "subgraph.lastAction"), @NamedAttributeNode(value = "submissionStatus", subgraph = "subgraph.submissionStatus"), @NamedAttributeNode(value = "organization", subgraph = "subgraph.organization"), // @NamedAttributeNode(value = "fieldValues", subgraph = "subgraph.fieldValues"), @@ -64,6 +64,13 @@ name = "subgraph.user", attributeNodes = {} ), + @NamedSubgraph( + name = "subgraph.lastAction", + attributeNodes = { + @NamedAttributeNode(value = "entry"), + @NamedAttributeNode(value = "actionDate"), + } + ), @NamedSubgraph( name = "subgraph.submissionStatus", attributeNodes = {} @@ -158,6 +165,10 @@ public class Submission extends ValidatingBaseEntity { @ManyToOne(fetch = LAZY, optional = true) private User assignee; + @JsonView(Views.SubmissionList.class) + @OneToOne(fetch = LAZY, optional = true) + private ActionLog lastAction; + @JsonView(Views.SubmissionList.class) @ManyToOne(fetch = LAZY, optional = false) private SubmissionStatus submissionStatus; @@ -295,6 +306,20 @@ public void setAssignee(User assignee) { this.assignee = assignee; } + /** + * @return the lastAction + */ + public ActionLog getLastAction() { + return lastAction; + } + + /** + * @param lastAction the last action to set + */ + public void setLastAction(ActionLog lastAction) { + this.lastAction = lastAction; + } + /** * @return the submissionStatus */ @@ -529,23 +554,6 @@ public void removeActionLog(ActionLog actionLog) { getActionLogs().remove(actionLog); } - /** - * - */ - @JsonView(Views.Partial.class) - public String getLastEvent() { - Optional actionLog = getActionLogs() - .stream() - .max(Comparator.comparing(al -> al.getActionDate())); - String lastEvent = null; - - if (actionLog.isPresent()) { - lastEvent = actionLog.get().getEntry(); - } - - return lastEvent; - } - /** * @return The reviewer notes. */ @@ -704,7 +712,7 @@ public List getFieldValuesByPredicateValueStartsWith(String predicat } }); return fieldValues; - } + } @JsonIgnore diff --git a/src/main/java/org/tdl/vireo/model/repo/SubmissionRepo.java b/src/main/java/org/tdl/vireo/model/repo/SubmissionRepo.java index 3be041b7b..b6448a8a5 100644 --- a/src/main/java/org/tdl/vireo/model/repo/SubmissionRepo.java +++ b/src/main/java/org/tdl/vireo/model/repo/SubmissionRepo.java @@ -17,7 +17,7 @@ public interface SubmissionRepo extends WeaverRepo, SubmissionRepoCu public List findByOrganization(Organization organization); - public List findByActionLogsId(Long id); + public Submission findByActionLogsId(Long id); @EntityGraph(value = "graph.Submission.Individual") public List findAllBySubmitterId(Long submitterId); diff --git a/src/main/java/org/tdl/vireo/model/repo/impl/ActionLogRepoImpl.java b/src/main/java/org/tdl/vireo/model/repo/impl/ActionLogRepoImpl.java index 8e40f7fff..bb717cff7 100644 --- a/src/main/java/org/tdl/vireo/model/repo/impl/ActionLogRepoImpl.java +++ b/src/main/java/org/tdl/vireo/model/repo/impl/ActionLogRepoImpl.java @@ -1,76 +1,92 @@ -package org.tdl.vireo.model.repo.impl; - -import static edu.tamu.weaver.response.ApiStatus.SUCCESS; - -import java.util.Calendar; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.messaging.simp.SimpMessagingTemplate; -import org.tdl.vireo.model.ActionLog; -import org.tdl.vireo.model.Submission; -import org.tdl.vireo.model.User; -import org.tdl.vireo.model.repo.ActionLogRepo; -import org.tdl.vireo.model.repo.SubmissionRepo; -import org.tdl.vireo.model.repo.custom.ActionLogRepoCustom; - -import edu.tamu.weaver.data.model.repo.impl.AbstractWeaverRepoImpl; -import edu.tamu.weaver.response.ApiResponse; - -public class ActionLogRepoImpl extends AbstractWeaverRepoImpl implements ActionLogRepoCustom { - - @Autowired - private ActionLogRepo actionLogRepo; - - @Autowired - private SubmissionRepo submissionRepo; - - @Autowired - private SimpMessagingTemplate simpMessagingTemplate; - - @Override - public ActionLog create(Submission submission, User user, Calendar actionDate, String entry, boolean privateFlag) { - ActionLog log = actionLogRepo.save(new ActionLog(submission.getSubmissionStatus(), user, actionDate, entry, privateFlag)); - submission.addActionLog(log); - submissionRepo.save(submission); - simpMessagingTemplate.convertAndSend("/channel/submission/" + submission.getId() + "/action-logs", new ApiResponse(SUCCESS, log)); - return log; - } - - @Override - public ActionLog create(Submission submission, Calendar actionDate, String entry, boolean privateFlag) { - ActionLog log = actionLogRepo.save(new ActionLog(submission.getSubmissionStatus(), actionDate, entry, privateFlag)); - submission.addActionLog(log); - submissionRepo.save(submission); - simpMessagingTemplate.convertAndSend("/channel/submission/" + submission.getId() + "/action-logs", new ApiResponse(SUCCESS, log)); - return log; - } - - @Override - public ActionLog createPublicLog(Submission submission, User user, String entry) { - return create(submission, user, Calendar.getInstance(), entry, false); - } - - @Override - public ActionLog createAdvisorPublicLog(Submission submission, String entry) { - return create(submission, Calendar.getInstance(), entry, false); - } - - @Override - public ActionLog createPrivateLog(Submission submission, User user, String entry) { - return create(submission, user, Calendar.getInstance(), entry, true); - } - - @Override - public void delete(ActionLog actionLog) { - for (Submission submission : submissionRepo.findByActionLogsId(actionLog.getId())) { - submission.removeActionLog(actionLog); - submissionRepo.save(submission); - } - } - - @Override - protected String getChannel() { - return "/channel/action-log"; - } - -} +package org.tdl.vireo.model.repo.impl; + +import static edu.tamu.weaver.response.ApiStatus.SUCCESS; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.tdl.vireo.model.ActionLog; +import org.tdl.vireo.model.Submission; +import org.tdl.vireo.model.User; +import org.tdl.vireo.model.repo.ActionLogRepo; +import org.tdl.vireo.model.repo.SubmissionRepo; +import org.tdl.vireo.model.repo.custom.ActionLogRepoCustom; + +import edu.tamu.weaver.data.model.repo.impl.AbstractWeaverRepoImpl; +import edu.tamu.weaver.response.ApiResponse; + +public class ActionLogRepoImpl extends AbstractWeaverRepoImpl implements ActionLogRepoCustom { + + @Autowired + private ActionLogRepo actionLogRepo; + + @Autowired + private SubmissionRepo submissionRepo; + + @Autowired + private SimpMessagingTemplate simpMessagingTemplate; + + @Override + public ActionLog create(Submission submission, User user, Calendar actionDate, String entry, boolean privateFlag) { + ActionLog log = actionLogRepo.save(new ActionLog(submission.getSubmissionStatus(), user, actionDate, entry, privateFlag)); + submission.addActionLog(log); + submission.setLastAction(log); + submissionRepo.save(submission); + simpMessagingTemplate.convertAndSend("/channel/submission/" + submission.getId() + "/action-logs", new ApiResponse(SUCCESS, log)); + return log; + } + + @Override + public ActionLog create(Submission submission, Calendar actionDate, String entry, boolean privateFlag) { + ActionLog log = actionLogRepo.save(new ActionLog(submission.getSubmissionStatus(), actionDate, entry, privateFlag)); + submission.addActionLog(log); + submission.setLastAction(log); + submissionRepo.save(submission); + simpMessagingTemplate.convertAndSend("/channel/submission/" + submission.getId() + "/action-logs", new ApiResponse(SUCCESS, log)); + return log; + } + + @Override + public ActionLog createPublicLog(Submission submission, User user, String entry) { + return create(submission, user, Calendar.getInstance(), entry, false); + } + + @Override + public ActionLog createAdvisorPublicLog(Submission submission, String entry) { + return create(submission, Calendar.getInstance(), entry, false); + } + + @Override + public ActionLog createPrivateLog(Submission submission, User user, String entry) { + return create(submission, user, Calendar.getInstance(), entry, true); + } + + @Override + public void delete(ActionLog actionLog) { + Submission submission = submissionRepo.findByActionLogsId(actionLog.getId()); + submission.removeActionLog(actionLog); + + List actionLogs = new ArrayList<>(submission.getActionLogs()); + + if (!actionLogs.isEmpty() && Objects.nonNull(submission.getLastAction()) + && submission.getLastAction().getId().equals(actionLog.getId())) { + actionLogs.sort(Comparator.comparing(ActionLog::getActionDate)); + submission.setLastAction(actionLogs.get(actionLogs.size() - 1)); + } else { + submission.setLastAction(null); + } + + submissionRepo.save(submission); + } + + @Override + protected String getChannel() { + return "/channel/action-log"; + } + +} diff --git a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java index ec4894c22..92ab74ca7 100644 --- a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java +++ b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java @@ -654,8 +654,7 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { if (submissionListColumn.getExactMatch()) { sqlBuilder.append("o").append(".name = '").append(filterString).append("'"); } else { - // TODO: determine if organization name will ever be - // search using a like + // TODO: determine if organization name will ever be search using a like sqlBuilder.append("LOWER(o").append(".name) LIKE '%").append(escapeString(filterString)).append("%'"); } @@ -745,6 +744,77 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { break; + case "lastAction.entry": + sqlBuilder = new StringBuilder(); + if (!sqlJoinsBuilder.toString().contains("LEFT JOIN action_log al ON al.id=s.last_action_id")) { + sqlBuilder.append("\nLEFT JOIN action_log al ON al.id=s.last_action_id"); + } + + sqlJoinsBuilder.append(sqlBuilder); + sqlCountSelectBuilder.append(sqlBuilder); + + if (submissionListColumn.getSortOrder() > 0) { + setColumnOrdering(submissionListColumn.getSort(), sqlAliasBuilders, sqlOrderBysBuilder, "al.entry"); + } + + for (String filterString : submissionListColumn.getFilters()) { + sqlBuilder = new StringBuilder(); + + if (filterString == null) { + sqlBuilder.append("al").append(".entry IS NULL"); + } else if (submissionListColumn.getExactMatch()) { + sqlBuilder.append("al").append(".entry = '").append(filterString).append("'"); + } else { + sqlBuilder.append("LOWER(al").append(".entry) LIKE '%").append(escapeString(filterString)).append("%'"); + } + + sqlWhereBuilderList.add(sqlBuilder); + getFromBuildersMap(sqlCountWhereFilterBuilders, "lastAction.entry").add(sqlBuilder); + } + + // all column search filter + for (String filterString : allColumnSearchFilters) { + sqlBuilder = new StringBuilder(); + sqlBuilder.append("LOWER(al").append(".entry) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlAllColumnsWhereBuilderList.add(sqlBuilder); + } + + break; + + case "lastAction.actionDate": + sqlBuilder = new StringBuilder(); + if (!sqlJoinsBuilder.toString().contains("LEFT JOIN action_log al ON al.id=s.last_action_id")) { + sqlBuilder.append("\nLEFT JOIN action_log al ON al.id=s.last_action_id"); + } + + sqlJoinsBuilder.append(sqlBuilder); + sqlCountSelectBuilder.append(sqlBuilder); + + if (submissionListColumn.getSortOrder() > 0) { + setColumnOrdering(submissionListColumn.getSort(), sqlAliasBuilders, sqlOrderBysBuilder, "al.action_date"); + } + + for (String filterString : submissionListColumn.getFilters()) { + if (filterString.contains("|")) { + String[] dates = filterString.split(Pattern.quote("|")); + sqlBuilder = new StringBuilder() + .append("al.").append("action_date") + .append(" BETWEEN CAST('").append(dates[0]) + .append("' AS DATE) AND CAST('").append(dates[1]) + .append("' AS DATE)"); + } else { + sqlBuilder = new StringBuilder() + .append("al.").append("action_date") + .append(" = CAST('").append(filterString) + .append("' AS DATE)"); + } + + sqlWhereBuilderList.add(sqlBuilder); + getFromBuildersMap(sqlCountWhereFilterBuilders, "lastAction.actionDate").add(sqlBuilder); + } + + break; + case "embargoTypes.name": // This is not a select column but is instead only a custom filter. if (submissionListColumn.getFilters().size() > 0) { @@ -809,23 +879,6 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { break; - case "lastEvent": - sqlBuilder = new StringBuilder() - .append("\nLEFT JOIN") - .append("\n (SELECT al.id, al.action_date, al.entry, al.action_logs_id") - .append("\n FROM action_log al") - .append("\n WHERE (al.action_logs_id = id)") - .append("\n ORDER BY al.action_date DESC") - .append("\n LIMIT 1) als") - .append("\n ON action_logs_id = s.submission_status_id"); - - sqlJoinsBuilder.append(sqlBuilder); - sqlCountSelectBuilder.append(sqlBuilder); - - // TODO: finish sqlWheresBuilder. - - break; - // exclude individual submissions from submission list case "exclude": @@ -1154,8 +1207,8 @@ protected String getChannel() { private StringBuilder buildSubmissionDateFieldString(String column, String filter) { if (filter.contains("|")) { String[] dates = filter.split(Pattern.quote("|")); - return new StringBuilder() - .append("s.").append(column) + return new StringBuilder() + .append("s.").append(column) .append(" BETWEEN CAST('").append(dates[0]) .append("' AS DATE) AND CAST('").append(dates[1]) .append("' AS DATE)"); diff --git a/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json b/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json index b81bf84e0..65a49df3e 100644 --- a/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json +++ b/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json @@ -162,7 +162,8 @@ "title": "Last Event", "sort": "NONE", "valuePath": [ - "lastEvent" + "lastAction", + "entry" ], "status": null, "inputType": { @@ -172,10 +173,13 @@ { "title": "Event Time", "sort": "NONE", - "valuePath": [], + "valuePath": [ + "lastAction", + "actionDate" + ], "status": null, "inputType": { - "name": "INPUT_TEXT" + "name": "INPUT_DATE" } }, { From 0f8fc872f20a8998ad63487f4aca937cc6eaa8b4 Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 30 Jul 2024 10:54:59 -0500 Subject: [PATCH 23/41] Make graduation semester sort method exception safe --- .../submission/submissionListController.js | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/app/controllers/submission/submissionListController.js b/src/main/webapp/app/controllers/submission/submissionListController.js index 0cfac6c3d..3d322994d 100644 --- a/src/main/webapp/app/controllers/submission/submissionListController.js +++ b/src/main/webapp/app/controllers/submission/submissionListController.js @@ -222,13 +222,32 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle var controlledVocabularies = ControlledVocabularyRepo.getAll(); var submissionTypeList = FieldValueRepo.findValuesByPredicateValue('submission_type'); var graduationSemesters = FieldValueRepo.findValuesByPredicateValue('dc.date.issued', function (a, b) { - const [monthA, yearA] = a.split(' '); - const [monthB, yearB] = b.split(' '); + try { + if (typeof a !== 'string' || typeof b !== 'string') { + throw new Error('Invalid input type. Both inputs must be strings.'); + } + + const regex = /^[A-Za-z]+\s\d{4}$/; + + if (!regex.test(a) || !regex.test(b)) { + throw new Error('Invalid input format. Inputs must be in the format "Month Year".'); + } - const dateA = new Date(`${monthA} 1, ${yearA}`); - const dateB = new Date(`${monthB} 1, ${yearB}`); + const [monthA, yearA] = a.split(' '); + const [monthB, yearB] = b.split(' '); - return dateB - dateA; + const dateA = new Date(`${monthA} 1, ${yearA}`); + const dateB = new Date(`${monthB} 1, ${yearB}`); + + if (isNaN(dateA) || isNaN(dateB)) { + throw new Error('Invalid date. Inputs must be valid dates.'); + } + + return dateB - dateA; + } catch (error) { + console.error(error.message); + return 0; + } }); var addBatchCommentEmail = function (message) { From 1218ca7c3f5c9568a98f9a0bbdbb8d1080576b96 Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 30 Jul 2024 11:16:33 -0500 Subject: [PATCH 24/41] Update role when admin email defined in properties --- .../service/VireoUserCredentialsService.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java b/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java index b8150ee7b..41a0dfcd8 100644 --- a/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java +++ b/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java @@ -68,15 +68,15 @@ public synchronized User updateUserByCredentials(Credentials credentials) { ? userRepo.findByNetid(shibNetid) : userRepo.findByEmail(shibEmail); - if (user == null) { - Role role = Role.ROLE_STUDENT; + Role role = Role.ROLE_STUDENT; - for (String email : admins) { - if (email.equals(shibEmail)) { - role = Role.ROLE_ADMIN; - } + for (String email : admins) { + if (email.equals(shibEmail)) { + role = Role.ROLE_ADMIN; } + } + if (user == null) { user = userRepo.create(shibEmail, shibFirstName, shibLastName, role); user.setNetid(shibNetid); @@ -136,6 +136,11 @@ public synchronized User updateUserByCredentials(Credentials credentials) { isUserUpdated = true; } + if (!user.getRole().equals(role)) { + user.setRole(role); + isUserUpdated = true; + } + if (isUserUpdated) { user = userRepo.save(user); } From 85e5528fe4bc63024be46be607e3e17c8d532c9c Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 30 Jul 2024 10:47:37 -0500 Subject: [PATCH 25/41] Check submission action logs have at least one entry to avoid null pointer --- .../controllers/submission/adminSubmissionViewController.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/app/controllers/submission/adminSubmissionViewController.js b/src/main/webapp/app/controllers/submission/adminSubmissionViewController.js index ca08b6e76..2bdacc0c1 100644 --- a/src/main/webapp/app/controllers/submission/adminSubmissionViewController.js +++ b/src/main/webapp/app/controllers/submission/adminSubmissionViewController.js @@ -591,7 +591,9 @@ vireo.controller("AdminSubmissionViewController", function ($anchorScroll, $cont }; var getLastActionLog = function() { - return $filter('orderBy')($scope.submission.actionLogs, 'actionDate', true)[0]; + if ($scope.submission.actionLogs.length > 0) { + return $filter('orderBy')($scope.submission.actionLogs, 'actionDate', true)[0]; + } } var getLastActionDate = function() { From 7b028343d255efc5c2ce472cc3b0d8f0492076cb Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 30 Jul 2024 10:40:19 -0500 Subject: [PATCH 26/41] Do not repeat yourself and use strict equality --- .../app/controllers/submission/submissionListController.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/app/controllers/submission/submissionListController.js b/src/main/webapp/app/controllers/submission/submissionListController.js index 91a8a32a4..5c0bc4e73 100644 --- a/src/main/webapp/app/controllers/submission/submissionListController.js +++ b/src/main/webapp/app/controllers/submission/submissionListController.js @@ -348,11 +348,13 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle }); }; + var hasSinglePredicate = (c) => c.predicate === undefined || c.predicate === null || c.predicate.trim().indexOf(' ') === -1; + var setFilterColumns = function(userFilterColumns, inactiveFilterColumns) { // exclude columns which have multiple predicates angular.extend(filterColumns, { - userFilterColumns: userFilterColumns.filter(c => (c.predicate === undefined || c.predicate == null) || c.predicate.trim().indexOf(' ') === -1), - inactiveFilterColumns: inactiveFilterColumns.filter(c => (c.predicate === undefined || c.predicate == null) || c.predicate.trim().indexOf(' ') === -1) + userFilterColumns: userFilterColumns.filter(hasSinglePredicate), + inactiveFilterColumns: inactiveFilterColumns.filter(hasSinglePredicate) }); } From fd364b6ca816e5e920870513a2f60b5fda020438 Mon Sep 17 00:00:00 2001 From: William Welling Date: Tue, 30 Jul 2024 10:35:48 -0500 Subject: [PATCH 27/41] Remove redundant string builder appends --- .../model/repo/impl/SubmissionRepoImpl.java | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java index 92ab74ca7..f2ef17d57 100644 --- a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java +++ b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java @@ -593,7 +593,7 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { for (String filterString : submissionListColumn.getFilters()) { sqlBuilder = new StringBuilder(); - sqlBuilder.append("s").append(".id = ").append(filterString); + sqlBuilder.append("s.id = ").append(filterString); sqlWhereBuilderList.add(sqlBuilder); getFromBuildersMap(sqlCountWhereFilterBuilders, "id").add(sqlBuilder); } @@ -615,10 +615,10 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { sqlBuilder = new StringBuilder(); if (submissionListColumn.getExactMatch()) { - sqlBuilder.append("ss").append(".name = '").append(filterString).append("'"); + sqlBuilder.append("ss.name = '").append(filterString).append("'"); } else { // TODO: determine if status will ever be search using a like - sqlBuilder.append("LOWER(ss").append(".name) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlBuilder.append("LOWER(ss.name) LIKE '%").append(escapeString(filterString)).append("%'"); } sqlWhereBuilderList.add(sqlBuilder); @@ -628,7 +628,7 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { // all column search filter for (String filterString : allColumnSearchFilters) { sqlBuilder = new StringBuilder(); - sqlBuilder.append("LOWER(ss").append(".name) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlBuilder.append("LOWER(ss.name) LIKE '%").append(escapeString(filterString)).append("%'"); sqlAllColumnsWhereBuilderList.add(sqlBuilder); } @@ -652,10 +652,10 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { sqlBuilder = new StringBuilder(); if (submissionListColumn.getExactMatch()) { - sqlBuilder.append("o").append(".name = '").append(filterString).append("'"); + sqlBuilder.append("o.name = '").append(filterString).append("'"); } else { // TODO: determine if organization name will ever be search using a like - sqlBuilder.append("LOWER(o").append(".name) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlBuilder.append("LOWER(o.name) LIKE '%").append(escapeString(filterString)).append("%'"); } sqlWhereBuilderList.add(sqlBuilder); @@ -665,7 +665,7 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { // all column search filter for (String filterString : allColumnSearchFilters) { sqlBuilder = new StringBuilder(); - sqlBuilder.append("LOWER(o").append(".name) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlBuilder.append("LOWER(o.name) LIKE '%").append(escapeString(filterString)).append("%'"); sqlAllColumnsWhereBuilderList.add(sqlBuilder); } @@ -689,11 +689,11 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { for (String filterString : submissionListColumn.getFilters()) { sqlBuilder = new StringBuilder(); if (submissionListColumn.getExactMatch()) { - sqlBuilder.append("oc").append(".name = '").append(filterString).append("'"); + sqlBuilder.append("oc.name = '").append(filterString).append("'"); } else { // TODO: determine if organization category name // will ever be search using a like - sqlBuilder.append("LOWER(oc").append(".name) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlBuilder.append("LOWER(oc.name) LIKE '%").append(escapeString(filterString)).append("%'"); } sqlWhereBuilderList.add(sqlBuilder); @@ -703,7 +703,7 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { // all column search filter for (String filterString : allColumnSearchFilters) { sqlBuilder = new StringBuilder(); - sqlBuilder.append("LOWER(oc").append(".name) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlBuilder.append("LOWER(oc.name) LIKE '%").append(escapeString(filterString)).append("%'"); sqlAllColumnsWhereBuilderList.add(sqlBuilder); } @@ -724,11 +724,11 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { sqlBuilder = new StringBuilder(); if (filterString == null) { - sqlBuilder.append("a").append(".email IS NULL"); + sqlBuilder.append("a.email IS NULL"); } else if (submissionListColumn.getExactMatch()) { - sqlBuilder.append("a").append(".email = '").append(filterString).append("'"); + sqlBuilder.append("a.email = '").append(filterString).append("'"); } else { - sqlBuilder.append("LOWER(a").append(".email) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlBuilder.append("LOWER(a.email) LIKE '%").append(escapeString(filterString)).append("%'"); } sqlWhereBuilderList.add(sqlBuilder); @@ -738,7 +738,7 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { // all column search filter for (String filterString : allColumnSearchFilters) { sqlBuilder = new StringBuilder(); - sqlBuilder.append("LOWER(a").append(".email) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlBuilder.append("LOWER(a.email) LIKE '%").append(escapeString(filterString)).append("%'"); sqlAllColumnsWhereBuilderList.add(sqlBuilder); } @@ -761,11 +761,11 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { sqlBuilder = new StringBuilder(); if (filterString == null) { - sqlBuilder.append("al").append(".entry IS NULL"); + sqlBuilder.append("al.entry IS NULL"); } else if (submissionListColumn.getExactMatch()) { - sqlBuilder.append("al").append(".entry = '").append(filterString).append("'"); + sqlBuilder.append("al.entry = '").append(filterString).append("'"); } else { - sqlBuilder.append("LOWER(al").append(".entry) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlBuilder.append("LOWER(al.entry) LIKE '%").append(escapeString(filterString)).append("%'"); } sqlWhereBuilderList.add(sqlBuilder); @@ -775,7 +775,7 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { // all column search filter for (String filterString : allColumnSearchFilters) { sqlBuilder = new StringBuilder(); - sqlBuilder.append("LOWER(al").append(".entry) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlBuilder.append("LOWER(al.entry) LIKE '%").append(escapeString(filterString)).append("%'"); sqlAllColumnsWhereBuilderList.add(sqlBuilder); } @@ -798,13 +798,13 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { if (filterString.contains("|")) { String[] dates = filterString.split(Pattern.quote("|")); sqlBuilder = new StringBuilder() - .append("al.").append("action_date") + .append("al.action_date") .append(" BETWEEN CAST('").append(dates[0]) .append("' AS DATE) AND CAST('").append(dates[1]) .append("' AS DATE)"); } else { sqlBuilder = new StringBuilder() - .append("al.").append("action_date") + .append("al.action_date") .append(" = CAST('").append(filterString) .append("' AS DATE)"); } @@ -888,9 +888,9 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { for (String filterString : submissionListColumn.getFilters()) { if (sqlWheresExcludeBuilder.length() > 0) { - sqlWheresExcludeBuilder.append(" AND s").append(".id <> ").append(filterString); + sqlWheresExcludeBuilder.append(" AND s.id <> ").append(filterString); } else { - sqlWheresExcludeBuilder.append(" s").append(".id <> ").append(filterString); + sqlWheresExcludeBuilder.append(" s.id <> ").append(filterString); } } @@ -975,7 +975,7 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { for (String filterString : submissionListColumn.getFilters()) { sqlBuilder = new StringBuilder(); - sqlBuilder.append("LOWER(s").append(".depositurl) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlBuilder.append("LOWER(s.depositurl) LIKE '%").append(escapeString(filterString)).append("%'"); sqlWhereBuilderList.add(sqlBuilder); getFromBuildersMap(sqlCountWhereFilterBuilders, "depositurl").add(sqlBuilder); } @@ -983,7 +983,7 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { // all column search filter for (String filterString : allColumnSearchFilters) { sqlBuilder = new StringBuilder(); - sqlBuilder.append("LOWER(s").append(".depositurl) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlBuilder.append("LOWER(s.depositurl) LIKE '%").append(escapeString(filterString)).append("%'"); sqlAllColumnsWhereBuilderList.add(sqlBuilder); } @@ -996,7 +996,7 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { for (String filterString : submissionListColumn.getFilters()) { sqlBuilder = new StringBuilder(); - sqlBuilder.append("LOWER(s").append(".reviewer_notes) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlBuilder.append("LOWER(s.reviewer_notes) LIKE '%").append(escapeString(filterString)).append("%'"); sqlWhereBuilderList.add(sqlBuilder); getFromBuildersMap(sqlCountWhereFilterBuilders, "reviewer_notes").add(sqlBuilder); } @@ -1004,7 +1004,7 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { // all column search filter for (String filterString : allColumnSearchFilters) { sqlBuilder = new StringBuilder(); - sqlBuilder.append("LOWER(s").append(".reviewer_notes) LIKE '%").append(escapeString(filterString)).append("%'"); + sqlBuilder.append("LOWER(s.reviewer_notes) LIKE '%").append(escapeString(filterString)).append("%'"); sqlAllColumnsWhereBuilderList.add(sqlBuilder); } From 490cfed9d00e04c9e5c9646456b3da8eb8f12ea8 Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 31 Jul 2024 03:05:22 -0500 Subject: [PATCH 28/41] Map submission list column field values --- .../java/org/tdl/vireo/model/Submission.java | 24 +++++- .../model/repo/impl/SubmissionRepoImpl.java | 85 ++++++++++++++----- .../submission/submissionListController.js | 65 ++++++-------- 3 files changed, 111 insertions(+), 63 deletions(-) diff --git a/src/main/java/org/tdl/vireo/model/Submission.java b/src/main/java/org/tdl/vireo/model/Submission.java index 6ce9d024b..d962bea1c 100644 --- a/src/main/java/org/tdl/vireo/model/Submission.java +++ b/src/main/java/org/tdl/vireo/model/Submission.java @@ -8,6 +8,7 @@ import java.util.Comparator; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -28,6 +29,7 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import javax.persistence.Transient; import javax.persistence.UniqueConstraint; import org.hibernate.annotations.Fetch; @@ -166,7 +168,7 @@ public class Submission extends ValidatingBaseEntity { @ManyToOne(fetch = LAZY, optional = false) private Organization organization; - @JsonView(Views.SubmissionList.class) + @JsonView(Views.SubmissionIndividual.class) @OneToMany(cascade = ALL, fetch = LAZY, orphanRemoval = true) private Set fieldValues; @@ -236,6 +238,10 @@ public class Submission extends ValidatingBaseEntity { @Column(nullable = true) private String depositURL; + @Transient + @JsonView(Views.SubmissionList.class) + private Map columnValues; + public Submission() { setModelValidator(new SubmissionValidator()); setFieldValues(new HashSet()); @@ -630,6 +636,20 @@ public void setDepositURL(String depositURL) { this.depositURL = depositURL; } + /** + * @return the list column values mapped from field values + */ + public Map getColumnValues() { + return columnValues; + } + + /** + * @param columnValues the list column values mapped from field values + */ + public void setColumnValues(Map columnValues) { + this.columnValues = columnValues; + } + /** * @param customActionValue */ @@ -704,7 +724,7 @@ public List getFieldValuesByPredicateValueStartsWith(String predicat } }); return fieldValues; - } + } @JsonIgnore diff --git a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java index 90c432117..f60e4e9c2 100644 --- a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java +++ b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java @@ -19,11 +19,17 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ExecutionException; +import java.util.function.Function; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -337,8 +343,8 @@ private void writeLicenseFile(User user, Submission submission, String licenseNa } @Override - public List batchDynamicSubmissionQuery(NamedSearchFilterGroup activeFilter, List submissionListColums) { - QueryStrings queryBuilder = craftDynamicSubmissionQuery(activeFilter, submissionListColums, null); + public List batchDynamicSubmissionQuery(NamedSearchFilterGroup activeFilter, List submissionListColumns) { + QueryStrings queryBuilder = craftDynamicSubmissionQuery(activeFilter, submissionListColumns, null); List ids = new ArrayList(); jdbcTemplate.queryForList(queryBuilder.getQuery()).forEach(row -> { ids.add((Long) row.get("ID")); @@ -348,14 +354,14 @@ public List batchDynamicSubmissionQuery(NamedSearchFilterGroup activ } @Override - public Page pageableDynamicSubmissionQuery(NamedSearchFilterGroup activeFilter, List submissionListColums, Pageable pageable) throws ExecutionException { + public Page pageableDynamicSubmissionQuery(NamedSearchFilterGroup activeFilter, List submissionListColumns, Pageable pageable) throws ExecutionException { long startTime = System.nanoTime(); - QueryStrings queryBuilder = craftDynamicSubmissionQuery(activeFilter, submissionListColums, pageable); + QueryStrings queryBuilder = craftDynamicSubmissionQuery(activeFilter, new ArrayList<>(submissionListColumns), pageable); Long total = jdbcTemplate.queryForObject(queryBuilder.getCountQuery(), Long.class); - logger.debug("Count query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); + logger.info("Count query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); startTime = System.nanoTime(); List ids = jdbcTemplate.query(queryBuilder.getQuery(), new RowMapper<>() { @@ -364,32 +370,71 @@ public Long mapRow(ResultSet rs, int rowNum) throws SQLException { } }); - logger.debug("ID query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); + logger.info("ID query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); startTime = System.nanoTime(); - List submissions = new ArrayList<>(); + List submissions = submissionRepo.findAllById(ids); - List unordered = submissionRepo.findAllById(ids); - - logger.debug("Find all query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); + logger.info("Find all query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); // order them - for (Long id : ids) { - for (Submission sub : unordered) { - if (sub.getId().equals(id)) { - submissions.add(sub); - unordered.remove(sub); - break; - } - } + Map idToIndexMap = new HashMap<>(); + for (int i = 0; i < ids.size(); i++) { + idToIndexMap.put(ids.get(i), i); } + submissions.sort((s1, s2) -> { + mapColumnValues(s1, submissionListColumns); + mapColumnValues(s2, submissionListColumns); + int index1 = idToIndexMap.get(s1.getId()); + int index2 = idToIndexMap.get(s2.getId()); + return Integer.compare(index1, index2); + }); + + logger.info("Sorting and mapping results took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); + int offset = pageable.getPageSize() * pageable.getPageNumber(); int limit = pageable.getPageSize(); return new PageImpl(submissions, PageRequest.of((int) Math.floor(offset / limit), limit), total); } - private QueryStrings craftDynamicSubmissionQuery(NamedSearchFilterGroup activeFilter, List submissionListColums, Pageable pageable) { + private void mapColumnValues(Submission submission, List submissionListColumns) { + if (Objects.isNull(submission.getColumnValues())) { + Map columnValues = new HashMap<>(); + + Map> groupedByPredicate = submission.getFieldValues().stream() + .collect(Collectors.groupingBy( + fv -> fv.getFieldPredicate().getValue(), + Collectors.mapping(FieldValue::getValue, Collectors.toList()) + )); + + submissionListColumns.stream().filter(slc -> StringUtils.isNotEmpty(slc.getPredicate())).forEach(slc -> { + StringBuilder value = new StringBuilder(); + + String predicate = slc.getPredicate().trim(); + + if (groupedByPredicate.containsKey(predicate)) { + value.append(String.join(", ", groupedByPredicate.get(predicate))); + } else { + // if the predicate is not found, see if it is space delimited set of predicates + if (predicate.contains(" ")) { + for (String p : predicate.split(" ")) { + String tp = p.replace(",", " ").trim(); + if (groupedByPredicate.containsKey(tp)) { + value.append(String.join(", ", groupedByPredicate.get(tp)) + (p.endsWith(",") ? ", " : " ")); + } + } + } + } + + columnValues.put(slc.getId(), value.toString()); + }); + + submission.setColumnValues(columnValues); + } + } + + private QueryStrings craftDynamicSubmissionQuery(NamedSearchFilterGroup activeFilter, List submissionListColumns, Pageable pageable) { // set up storage for user's preferred columns Set allColumnSearchFilters = new HashSet<>(); @@ -399,7 +444,7 @@ private QueryStrings craftDynamicSubmissionQuery(NamedSearchFilterGroup activeFi // set sort and sort order on all submission list columns that are set // on the requesting user's submission list columns - submissionListColums.forEach(submissionListColumn -> { + submissionListColumns.forEach(submissionListColumn -> { for (SubmissionListColumn slc : allSubmissionListColumns) { if (submissionListColumn.equals(slc)) { slc.setVisible(true); diff --git a/src/main/webapp/app/controllers/submission/submissionListController.js b/src/main/webapp/app/controllers/submission/submissionListController.js index 5c0bc4e73..774cf78a6 100644 --- a/src/main/webapp/app/controllers/submission/submissionListController.js +++ b/src/main/webapp/app/controllers/submission/submissionListController.js @@ -546,36 +546,15 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle var getValueFromArray = function (array, col) { var value = ""; - var predicates = col.predicate.split(' '); - var pv; - for (var v of predicates) { - var predicate = v.replace(',', '').trim(); - - var delimeter = ', '; - for (var j in array) { - var member = array[j]; - if (member.fieldPredicate !== undefined) { - if (member.fieldPredicate.value === predicate) { - - // if set of predicates, determine delimeter by precense of trailing comma - if (predicates.length > 1) { - delimeter = (!!pv && pv.endsWith(',')) ? ', ' : ' '; - } - - value += value.length > 0 ? delimeter + member.value : member.value; - } - } else { - var path = col.valuePath; - var curr = member; - for (var p = 1; p < path.length; p++) { - curr = curr[path[p]]; - } - value += value.length > 0 ? ', ' + curr : curr; - } + for (var j in array) { + var member = array[j]; + var path = col.valuePath; + var curr = member; + for (var p = 1; p < path.length; p++) { + curr = curr[path[p]]; } - pv = v; + value += value.length > 0 ? ", " + curr : curr; } - return value; }; @@ -687,26 +666,30 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle }; $scope.getSubmissionProperty = function (row, col) { - var value; - for (var i in col.valuePath) { - if(typeof col.valuePath[i] !== 'function') { - if (value === undefined) { - value = row[col.valuePath[i]]; - } else { - if (value instanceof Array) { - return getValueFromArray(value, col); + var value = row.columnValues[col.id]; + // use value path to get values + if (value === undefined) { + for (var i in col.valuePath) { + if (typeof col.valuePath[i] !== 'function') { + if (value === undefined) { + value = row[col.valuePath[i]]; } else { - if (value !== null) { - if (col.valuePath[0] === "assignee") { - value = getAssigneeDisplayName(row); - } else { - value = value[col.valuePath[i]]; + if (value instanceof Array) { + return getValueFromArray(value, col); + } else { + if (value !== null) { + if (col.valuePath[0] === "assignee") { + value = getAssigneeDisplayName(row); + } else { + value = value[col.valuePath[i]]; + } } } } } } } + return value; }; From cebc35b11c5676f1d2a1679a789d375b944baec1 Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 31 Jul 2024 03:30:12 -0500 Subject: [PATCH 29/41] Restore further filter by graduation semester as date --- .../furtherFilterByDegreeDate.html | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByDegreeDate.html b/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByDegreeDate.html index bfed4b9f9..cac0a5ac1 100644 --- a/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByDegreeDate.html +++ b/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByDegreeDate.html @@ -1,10 +1,20 @@ -
- + +
+ + + + +
+
From f4f6d00ed7e06989d4482393f502c85b2b120e68 Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 31 Jul 2024 11:36:58 -0500 Subject: [PATCH 30/41] Add filter by graduation semester as list --- .../SYSTEM_Default_Submission_List_Columns.json | 10 ++++++++++ .../controllers/submission/submissionListController.js | 2 +- .../sideboxes/furtherFilterBy/furtherFilterBy.html | 4 ++++ .../furtherFilterBy/furtherFilterByDegreeDateList.html | 10 ++++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByDegreeDateList.html diff --git a/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json b/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json index b81bf84e0..841ffec7b 100644 --- a/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json +++ b/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json @@ -221,5 +221,15 @@ "inputType": { "name": "INPUT_TEXT" } + }, + { + "title": "Graduation Semester (List)", + "sort": "NONE", + "predicate": "dc.date.issued", + "valuePath": [], + "status": null, + "inputType": { + "name": "INPUT_DEGREEDATE" + } } ] diff --git a/src/main/webapp/app/controllers/submission/submissionListController.js b/src/main/webapp/app/controllers/submission/submissionListController.js index 3d322994d..6efd7327f 100644 --- a/src/main/webapp/app/controllers/submission/submissionListController.js +++ b/src/main/webapp/app/controllers/submission/submissionListController.js @@ -164,7 +164,7 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle if (!!allSubmissionListFilters) { submissionListColumns = allSubmissionListFilters.filter(function excludeCustomFilters(slc) { - return slc.title !== 'Search Box' && slc.title !== "Submission Type (List)" && slc.title !== "Embargo Type"; + return slc.title !== 'Search Box' && slc.title !== "Submission Type (List)" && slc.title !== "Graduation Semester (List)" && slc.title !== "Embargo Type"; }); submissionListColumnsForManage = allSubmissionListFilters.filter(function excludeSearchBox(slc) { diff --git a/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterBy.html b/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterBy.html index 73da63516..6c2e23ae8 100644 --- a/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterBy.html +++ b/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterBy.html @@ -43,6 +43,10 @@
+
+
+
+
diff --git a/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByDegreeDateList.html b/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByDegreeDateList.html new file mode 100644 index 000000000..b258c2db9 --- /dev/null +++ b/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByDegreeDateList.html @@ -0,0 +1,10 @@ +
+ +
From a7620e8ba4664a6abd2091cbc3f6aa93f3a05a37 Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 31 Jul 2024 12:28:17 -0500 Subject: [PATCH 31/41] Coalesce named search filter group by first selected filter from submission list column --- .../controller/SubmissionListController.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/tdl/vireo/controller/SubmissionListController.java b/src/main/java/org/tdl/vireo/controller/SubmissionListController.java index daded5bba..3a969c96b 100644 --- a/src/main/java/org/tdl/vireo/controller/SubmissionListController.java +++ b/src/main/java/org/tdl/vireo/controller/SubmissionListController.java @@ -4,12 +4,11 @@ import static edu.tamu.weaver.response.ApiStatus.SUCCESS; import static org.springframework.web.bind.annotation.RequestMethod.POST; -import edu.tamu.weaver.auth.annotation.WeaverUser; -import edu.tamu.weaver.response.ApiResponse; -import edu.tamu.weaver.validation.aspect.annotation.WeaverValidatedModel; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -36,6 +35,10 @@ import org.tdl.vireo.model.repo.UserRepo; import org.tdl.vireo.service.DefaultSubmissionListColumnService; +import edu.tamu.weaver.auth.annotation.WeaverUser; +import edu.tamu.weaver.response.ApiResponse; +import edu.tamu.weaver.validation.aspect.annotation.WeaverValidatedModel; + @RestController @RequestMapping("/submission-list") public class SubmissionListController { @@ -228,7 +231,18 @@ public ApiResponse addFilterCriterion(@WeaverUser User user, @RequestBody Map Date: Wed, 31 Jul 2024 12:46:09 -0500 Subject: [PATCH 32/41] Update system data loader test for additional system submission list column --- src/test/java/org/tdl/vireo/service/SystemDataLoaderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/tdl/vireo/service/SystemDataLoaderTest.java b/src/test/java/org/tdl/vireo/service/SystemDataLoaderTest.java index 38a3c0c04..4b9fa9f5e 100644 --- a/src/test/java/org/tdl/vireo/service/SystemDataLoaderTest.java +++ b/src/test/java/org/tdl/vireo/service/SystemDataLoaderTest.java @@ -492,7 +492,7 @@ private void assertSubmissionStatusTransitions(String[] expected, SubmissionStat } private void assertSubmissionListColumn(boolean isReload) { - assertEquals(62, submissionListColumnRepo.count(), + assertEquals(63, submissionListColumnRepo.count(), isReload ? "Incorrect number of submissionListColumn found after reload" : "Incorrect number of submissionListColumn found"); From 271cf01837cbd110c2f36d095aab10737ca5d025 Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 31 Jul 2024 14:53:14 -0500 Subject: [PATCH 33/41] Update submission list controller tests --- src/main/webapp/tests/mocks/models/mockSubmission.js | 6 ++++++ .../submission/submissionListControllerTest.js | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/tests/mocks/models/mockSubmission.js b/src/main/webapp/tests/mocks/models/mockSubmission.js index 49fdc00d9..4fddc9ab1 100644 --- a/src/main/webapp/tests/mocks/models/mockSubmission.js +++ b/src/main/webapp/tests/mocks/models/mockSubmission.js @@ -13,6 +13,7 @@ var dataSubmission1 = { customActionValues: [], depositURL: "", fieldValues: [], + columnValues: [], organization: { id: 1, acceptsSubmissions: true, @@ -73,6 +74,7 @@ var dataSubmission2 = { customActionValues: [], depositURL: "", fieldValues: [], + columnValues: [], organization: { id: 2, acceptsSubmissions: false, @@ -124,6 +126,7 @@ var dataSubmission3 = { customActionValues: [], depositURL: "", fieldValues: [], + columnValues: [], organization: { id: 1, acceptsSubmissions: true, @@ -184,6 +187,7 @@ var dataSubmission4 = { customActionValues: [], depositURL: "", fieldValues: [], + columnValues: [], organization: { id: 1, acceptsSubmissions: true, @@ -225,6 +229,7 @@ var dataSubmission5 = { customActionValues: [], depositURL: "", fieldValues: [], + columnValues: [], organization: { id: 1, acceptsSubmissions: true, @@ -266,6 +271,7 @@ var dataSubmission6 = { customActionValues: [], depositURL: "", fieldValues: [], + columnValues: [], organization: { id: 2, acceptsSubmissions: false, diff --git a/src/main/webapp/tests/unit/controllers/submission/submissionListControllerTest.js b/src/main/webapp/tests/unit/controllers/submission/submissionListControllerTest.js index 2e283c307..d1f412806 100644 --- a/src/main/webapp/tests/unit/controllers/submission/submissionListControllerTest.js +++ b/src/main/webapp/tests/unit/controllers/submission/submissionListControllerTest.js @@ -352,8 +352,8 @@ describe("controller: SubmissionListController", function () { expect(scope.columns.length).toBe(1); }); it("displaySubmissionProperty should return a value", function () { - var row = {}; - var column = { inputType: mockInputType(q) }; + var row = { columnValues: [] }; + var column = { inputType: mockInputType(q), id: 1 }; scope.displaySubmissionProperty(row, column); @@ -399,8 +399,8 @@ describe("controller: SubmissionListController", function () { }); it("getSubmissionProperty should return a property", function () { var response; - var row = { assignee: mockUser(q) }; - var column = { valuePath: [ "assignee" ] }; + var row = { assignee: mockUser(q), columnValues: [] }; + var column = { valuePath: [ "assignee" ], id: 1 }; response = scope.getSubmissionProperty(row, column); expect(response).toEqual(row.assignee); From b8fd6d9393557f7f3df956ebe7a613460a45b389 Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 31 Jul 2024 15:47:47 -0500 Subject: [PATCH 34/41] Update both username and email when shibEmail changes --- .../org/tdl/vireo/auth/service/VireoUserCredentialsService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java b/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java index 41a0dfcd8..219af96ef 100644 --- a/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java +++ b/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java @@ -132,6 +132,7 @@ public synchronized User updateUserByCredentials(Credentials credentials) { } if (StringUtils.isNotEmpty(shibEmail) && !user.getUsername().equals(shibEmail)) { + user.setEmail(shibEmail); user.setUsername(shibEmail); isUserUpdated = true; } From c1901d68841c091380895dd17c1b6f425de5e22c Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 31 Jul 2024 15:48:07 -0500 Subject: [PATCH 35/41] Null check submission list column on named search filter --- .../org/tdl/vireo/controller/SubmissionListController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/tdl/vireo/controller/SubmissionListController.java b/src/main/java/org/tdl/vireo/controller/SubmissionListController.java index 3a969c96b..0e43a70d1 100644 --- a/src/main/java/org/tdl/vireo/controller/SubmissionListController.java +++ b/src/main/java/org/tdl/vireo/controller/SubmissionListController.java @@ -233,7 +233,8 @@ public ApiResponse addFilterCriterion(@WeaverUser User user, @RequestBody Map Date: Wed, 31 Jul 2024 15:54:41 -0500 Subject: [PATCH 36/41] Fail gracefully when columnValues are not defined in response to dynamic submission query --- .../controllers/submission/submissionListController.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/app/controllers/submission/submissionListController.js b/src/main/webapp/app/controllers/submission/submissionListController.js index 2cda17ea0..96262e07d 100644 --- a/src/main/webapp/app/controllers/submission/submissionListController.js +++ b/src/main/webapp/app/controllers/submission/submissionListController.js @@ -702,8 +702,14 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle }; $scope.getSubmissionProperty = function (row, col) { - var value = row.columnValues[col.id]; - // use value path to get values + var value; + if (row.columnValues !== undefined) { + value = row.columnValues[col.id]; + } else { + console.error('row.columnValues is undefined') + } + + // check value path to get values if (value === undefined) { for (var i in col.valuePath) { if (typeof col.valuePath[i] !== 'function') { From 8bf24446ea35fb0cc8e1dfc6b49a68963aa37a1f Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 31 Jul 2024 15:55:03 -0500 Subject: [PATCH 37/41] Remove unused imports and replace stream lambda with for loop --- .../model/repo/impl/SubmissionRepoImpl.java | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java index 3fe3ec705..e5094b3c0 100644 --- a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java +++ b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java @@ -19,16 +19,13 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutionException; -import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -408,27 +405,29 @@ private void mapColumnValues(Submission submission, List s Collectors.mapping(FieldValue::getValue, Collectors.toList()) )); - submissionListColumns.stream().filter(slc -> StringUtils.isNotEmpty(slc.getPredicate())).forEach(slc -> { - StringBuilder value = new StringBuilder(); - - String predicate = slc.getPredicate().trim(); - - if (groupedByPredicate.containsKey(predicate)) { - value.append(String.join(", ", groupedByPredicate.get(predicate))); - } else { - // if the predicate is not found, see if it is space delimited set of predicates - if (predicate.contains(" ")) { - for (String p : predicate.split(" ")) { - String tp = p.replace(",", " ").trim(); - if (groupedByPredicate.containsKey(tp)) { - value.append(String.join(", ", groupedByPredicate.get(tp)) + (p.endsWith(",") ? ", " : " ")); + for (SubmissionListColumn slc : submissionListColumns) { + if (StringUtils.isNotEmpty(slc.getPredicate())) { + StringBuilder value = new StringBuilder(); + + String predicate = slc.getPredicate().trim(); + + if (groupedByPredicate.containsKey(predicate)) { + value.append(String.join(", ", groupedByPredicate.get(predicate))); + } else { + // if the predicate is not found, see if it is space delimited set of predicates + if (predicate.contains(" ")) { + for (String p : predicate.split(" ")) { + String tp = p.replace(",", " ").trim(); + if (groupedByPredicate.containsKey(tp)) { + value.append(String.join(", ", groupedByPredicate.get(tp)) + (p.endsWith(",") ? ", " : " ")); + } } } } - } - columnValues.put(slc.getId(), value.toString()); - }); + columnValues.put(slc.getId(), value.toString()); + } + } submission.setColumnValues(columnValues); } From 497c7d851583bca65ad7fe8353f17eb9805c0bf1 Mon Sep 17 00:00:00 2001 From: William Welling Date: Thu, 1 Aug 2024 08:43:41 -0500 Subject: [PATCH 38/41] Switch performance logging back to debug --- .../org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java index e5094b3c0..e50b7804e 100644 --- a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java +++ b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java @@ -358,7 +358,7 @@ public Page pageableDynamicSubmissionQuery(NamedSearchFilterGroup ac Long total = jdbcTemplate.queryForObject(queryBuilder.getCountQuery(), Long.class); - logger.info("Count query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); + logger.debug("Count query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); startTime = System.nanoTime(); List ids = jdbcTemplate.query(queryBuilder.getQuery(), new RowMapper<>() { @@ -367,12 +367,12 @@ public Long mapRow(ResultSet rs, int rowNum) throws SQLException { } }); - logger.info("ID query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); + logger.debug("ID query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); startTime = System.nanoTime(); List submissions = submissionRepo.findAllById(ids); - logger.info("Find all query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); + logger.debug("Find all query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); // order them Map idToIndexMap = new HashMap<>(); @@ -388,7 +388,7 @@ public Long mapRow(ResultSet rs, int rowNum) throws SQLException { return Integer.compare(index1, index2); }); - logger.info("Sorting and mapping results took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); + logger.debug("Sorting and mapping results took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds"); int offset = pageable.getPageSize() * pageable.getPageNumber(); int limit = pageable.getPageSize(); From 1062d37527cc49c2d7a16cfa84f050df9ad9b269 Mon Sep 17 00:00:00 2001 From: William Welling Date: Thu, 1 Aug 2024 09:53:00 -0500 Subject: [PATCH 39/41] Increment patch release as snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 44b48e342..3281a3ff7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.tdl vireo - 4.2.7 + 4.2.8-SNAPSHOT Vireo Vireo Thesis and Dissertation Submission System From 1fd1efa02781276c76abab5a369a05d6073bc219 Mon Sep 17 00:00:00 2001 From: William Welling Date: Thu, 1 Aug 2024 11:57:35 -0500 Subject: [PATCH 40/41] Remove snapshot from version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3281a3ff7..0b52d5a30 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.tdl vireo - 4.2.8-SNAPSHOT + 4.2.8 Vireo Vireo Thesis and Dissertation Submission System From b0b182da46dd0de0be70d9d0d8788e9920e20477 Mon Sep 17 00:00:00 2001 From: William Welling Date: Fri, 2 Aug 2024 14:41:52 -0500 Subject: [PATCH 41/41] Fix user role being reassigned on login --- .../service/VireoUserCredentialsService.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java b/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java index 219af96ef..fc8b442b5 100644 --- a/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java +++ b/src/main/java/org/tdl/vireo/auth/service/VireoUserCredentialsService.java @@ -68,15 +68,15 @@ public synchronized User updateUserByCredentials(Credentials credentials) { ? userRepo.findByNetid(shibNetid) : userRepo.findByEmail(shibEmail); - Role role = Role.ROLE_STUDENT; + if (user == null) { + Role role = Role.ROLE_STUDENT; - for (String email : admins) { - if (email.equals(shibEmail)) { - role = Role.ROLE_ADMIN; + for (String email : admins) { + if (email.equals(shibEmail)) { + role = Role.ROLE_ADMIN; + } } - } - if (user == null) { user = userRepo.create(shibEmail, shibFirstName, shibLastName, role); user.setNetid(shibNetid); @@ -137,11 +137,6 @@ public synchronized User updateUserByCredentials(Credentials credentials) { isUserUpdated = true; } - if (!user.getRole().equals(role)) { - user.setRole(role); - isUserUpdated = true; - } - if (isUserUpdated) { user = userRepo.save(user); }