diff --git a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/BaseServiceIT.java b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/BaseServiceIT.java index b8239010e..21e4349a8 100644 --- a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/BaseServiceIT.java +++ b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/BaseServiceIT.java @@ -73,6 +73,17 @@ public void setup() { } } + /** + * Resets the Spring Security context with a given principal and user roles. + * + *
This method sets the specified principal (user identifier) and roles
+ * for the security context, effectively simulating an authenticated user
+ * in the system for testing or execution purposes. It clears the existing
+ * security context and creates a new one with the provided principal and roles.
+ *
+ * @param principal The principal (typically the username or identifier of the user) to set in the security context.
+ * @param role One or more {@link UserRole} values representing the roles or authorities to assign to the principal.
+ */
protected void resetSecurityContext(String principal, UserRole... role) {
simplePrincipalProvider.setPrincipal(principal);
SecurityContext ctx = SecurityContextHolder.createEmptyContext();
diff --git a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/suggestions/BaseChangeSuggestionServiceIT.java b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/suggestions/BaseChangeSuggestionServiceIT.java
index 6ff3fe574..7a63e4bc0 100644
--- a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/suggestions/BaseChangeSuggestionServiceIT.java
+++ b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/suggestions/BaseChangeSuggestionServiceIT.java
@@ -181,6 +181,7 @@ public void updateEntityChangeSuggestionTest() {
// suggested changes
int numberChanges = updateEntity(entity);
address.setCity("city");
+ numberChanges++;
R suggestion = createEmptyChangeSuggestion();
suggestion.setSuggestedEntity(entity);
diff --git a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/suggestions/CollectionChangeSuggestionServiceIT.java b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/suggestions/CollectionChangeSuggestionServiceIT.java
index 8bd83475b..48c34ea6d 100644
--- a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/suggestions/CollectionChangeSuggestionServiceIT.java
+++ b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/suggestions/CollectionChangeSuggestionServiceIT.java
@@ -18,6 +18,8 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.gbif.api.model.collections.Address;
+import static org.gbif.registry.service.collections.utils.MasterSourceUtils.IH_SYNC_USER;
+
import org.gbif.api.model.collections.AlternativeCode;
import org.gbif.api.model.collections.Collection;
import org.gbif.api.model.collections.Contact;
@@ -35,7 +37,6 @@
import org.gbif.api.vocabulary.collections.IdType;
import org.gbif.api.vocabulary.collections.MasterSourceType;
import org.gbif.registry.service.collections.suggestions.CollectionChangeSuggestionService;
-import static org.gbif.registry.service.collections.utils.MasterSourceUtils.IH_SYNC_USER;
import org.gbif.ws.client.filter.SimplePrincipalProvider;
import java.net.URI;
diff --git a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/suggestions/InstitutionChangeSuggestionServiceIT.java b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/suggestions/InstitutionChangeSuggestionServiceIT.java
index a03aedc47..4e39628b8 100644
--- a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/suggestions/InstitutionChangeSuggestionServiceIT.java
+++ b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/suggestions/InstitutionChangeSuggestionServiceIT.java
@@ -127,7 +127,7 @@ int updateEntity(Institution entity) {
entity.setIdentifiers(Collections.singletonList(new Identifier(IdentifierType.LSID, "test")));
entity.setAlternativeCodes(
Collections.singletonList(new AlternativeCode(UUID.randomUUID().toString(), "test")));
- return 5;
+ return 4;
}
@Override
diff --git a/registry-service/src/main/java/org/gbif/registry/service/collections/suggestions/BaseChangeSuggestionService.java b/registry-service/src/main/java/org/gbif/registry/service/collections/suggestions/BaseChangeSuggestionService.java
index dfcd2f3e6..990a9c200 100644
--- a/registry-service/src/main/java/org/gbif/registry/service/collections/suggestions/BaseChangeSuggestionService.java
+++ b/registry-service/src/main/java/org/gbif/registry/service/collections/suggestions/BaseChangeSuggestionService.java
@@ -13,11 +13,23 @@
*/
package org.gbif.registry.service.collections.suggestions;
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.gbif.registry.security.UserRoles.*;
+import static org.gbif.registry.service.collections.utils.MasterSourceUtils.COLLECTION_LOCKABLE_FIELDS;
+import static org.gbif.registry.service.collections.utils.MasterSourceUtils.IH_SYNC_USER;
+import static org.gbif.registry.service.collections.utils.MasterSourceUtils.hasExternalMasterSource;
+
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;
-import org.gbif.api.model.collections.Collection;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.ParameterizedType;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
import org.gbif.api.model.collections.*;
+import org.gbif.api.model.collections.Collection;
import org.gbif.api.model.collections.suggestions.Change;
import org.gbif.api.model.collections.suggestions.ChangeSuggestion;
import org.gbif.api.model.collections.suggestions.CollectionChangeSuggestion;
@@ -36,6 +48,7 @@
import org.gbif.api.service.collections.CrudService;
import org.gbif.api.vocabulary.Country;
import org.gbif.api.vocabulary.UserRole;
+import org.gbif.api.vocabulary.collections.MasterSourceType;
import org.gbif.registry.events.EventManager;
import org.gbif.registry.events.collections.EventType;
import org.gbif.registry.events.collections.SubEntityCollectionEvent;
@@ -49,6 +62,8 @@
import org.gbif.registry.persistence.mapper.collections.dto.ChangeSuggestionDto;
import org.gbif.registry.security.grscicoll.GrSciCollAuthorizationService;
import org.gbif.registry.service.collections.merge.MergeService;
+import org.gbif.registry.service.collections.utils.MasterSourceUtils;
+
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@@ -57,18 +72,6 @@
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.ParameterizedType;
-import java.math.BigDecimal;
-import java.util.*;
-import java.util.stream.Collectors;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static org.gbif.registry.security.UserRoles.*;
-import static org.gbif.registry.service.collections.utils.MasterSourceUtils.IH_SYNC_USER;
-import static org.gbif.registry.service.collections.utils.MasterSourceUtils.hasExternalMasterSource;
-
public abstract class BaseChangeSuggestionService<
T extends
CollectionEntity & Taggable & Identifiable & MachineTaggable & Commentable & Contactable
@@ -305,15 +308,17 @@ protected ChangeSuggestionDto createMergeSuggestionDto(R changeSuggestion) {
public void updateChangeSuggestion(R updatedChangeSuggestion) {
ChangeSuggestionDto dto = changeSuggestionMapper.get(updatedChangeSuggestion.getKey());
- //checkArgument(
- //updatedChangeSuggestion.getComments().size() > dto.getComments().size(),
- //"A comment is required");
+ checkArgument(
+ updatedChangeSuggestion.getComments().size() > dto.getComments().size(),
+ "A comment is required");
if (dto.getType() == Type.CREATE || dto.getType() == Type.UPDATE) {
// we get the current entity from the DB to update the suggested entity with the current state
// and minimize the risk of having race conditions
R changeSuggestion = dtoToChangeSuggestion(dto);
+ lockFields(changeSuggestion, updatedChangeSuggestion);
+
Set