From 72a0cb34cd24f6121d0fbacd2877e4c5e607810b Mon Sep 17 00:00:00 2001 From: veltrup Date: Mon, 5 Feb 2024 08:32:25 +0100 Subject: [PATCH] feat: resolve identifier --- .../core/domain/entity/Identifier.java | 62 +++++++++++++++++++ .../exception/InvalidAnchorException.java | 9 ++- .../domain/service/IdentifierResolver.java | 32 ++++++++++ .../userrepository/core/usecase/GetUser.java | 10 ++- .../core/usecase/PurgeUser.java | 10 ++- .../core/domain/entity/IdentifierTest.java | 61 ++++++++++++++++++ .../service/IdentifierResolverTest.java | 59 ++++++++++++++++++ .../core/usecase/GetUserTest.java | 41 ++++++++++-- .../core/usecase/PurgeUserTest.java | 12 +++- 9 files changed, 287 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/sitepark/ies/userrepository/core/domain/entity/Identifier.java create mode 100644 src/main/java/com/sitepark/ies/userrepository/core/domain/service/IdentifierResolver.java create mode 100644 src/test/java/com/sitepark/ies/userrepository/core/domain/entity/IdentifierTest.java create mode 100644 src/test/java/com/sitepark/ies/userrepository/core/domain/service/IdentifierResolverTest.java diff --git a/src/main/java/com/sitepark/ies/userrepository/core/domain/entity/Identifier.java b/src/main/java/com/sitepark/ies/userrepository/core/domain/entity/Identifier.java new file mode 100644 index 0000000..23f2612 --- /dev/null +++ b/src/main/java/com/sitepark/ies/userrepository/core/domain/entity/Identifier.java @@ -0,0 +1,62 @@ +package com.sitepark.ies.userrepository.core.domain.entity; + +import java.util.Optional; + +public final class Identifier { + + private final Long id; + + private final Anchor anchor; + + private Identifier(Long id) { + this.id = id; + this.anchor = null; + } + + private Identifier(Anchor anchor) { + this.id = null; + this.anchor = anchor; + } + + public static Identifier ofId(long id) { + return new Identifier(id); + } + + public static Identifier ofAnchor(Anchor anchor) { + if (anchor == null) { + throw new NullPointerException("anchor is null"); + } + return new Identifier(anchor); + } + + public static Identifier ofString(String identifier) { + if (isId(identifier)) { + return new Identifier(Long.valueOf(identifier)); + } + return new Identifier(Anchor.ofString(identifier)); + } + + public Optional getId() { + return Optional.ofNullable(this.id); + } + + public Optional getAnchor() { + return Optional.ofNullable(this.anchor); + } + + private static boolean isId(String str) { + + int length = str.length(); + if (length > 19) { + return false; + } + + for (int i = 0; i < length; i++) { + char c = str.charAt(i); + if (c < '0' || c > '9') { + return false; + } + } + return true; + } +} diff --git a/src/main/java/com/sitepark/ies/userrepository/core/domain/exception/InvalidAnchorException.java b/src/main/java/com/sitepark/ies/userrepository/core/domain/exception/InvalidAnchorException.java index 21adda2..852580e 100644 --- a/src/main/java/com/sitepark/ies/userrepository/core/domain/exception/InvalidAnchorException.java +++ b/src/main/java/com/sitepark/ies/userrepository/core/domain/exception/InvalidAnchorException.java @@ -10,6 +10,12 @@ public class InvalidAnchorException extends UserRepositoryException { private static final long serialVersionUID = 1L; + public InvalidAnchorException(String name) { + super(); + this.name = name; + } + + public InvalidAnchorException(String name, String message) { super(message); this.name = name; @@ -21,6 +27,7 @@ public String getName() { @Override public String getMessage() { - return "Invalid anchor '" + this.name + ": " + super.getMessage(); + return "Invalid anchor '" + this.name + + (super.getMessage() != null ? ": " + super.getMessage() : ""); } } diff --git a/src/main/java/com/sitepark/ies/userrepository/core/domain/service/IdentifierResolver.java b/src/main/java/com/sitepark/ies/userrepository/core/domain/service/IdentifierResolver.java new file mode 100644 index 0000000..1f014d3 --- /dev/null +++ b/src/main/java/com/sitepark/ies/userrepository/core/domain/service/IdentifierResolver.java @@ -0,0 +1,32 @@ +package com.sitepark.ies.userrepository.core.domain.service; + +import java.util.Optional; + +import javax.inject.Inject; + +import com.sitepark.ies.userrepository.core.domain.entity.Identifier; +import com.sitepark.ies.userrepository.core.domain.exception.AnchorNotFoundException; +import com.sitepark.ies.userrepository.core.port.UserRepository; + +public class IdentifierResolver { + + private final UserRepository repository; + + @Inject + protected IdentifierResolver(UserRepository repository) { + this.repository = repository; + } + + public long resolveIdentifier(Identifier identifier) { + + if (identifier.getId().isPresent()) { + return identifier.getId().get(); + } + + Optional id = this.repository.resolveAnchor(identifier.getAnchor().get()); + if (id.isEmpty()) { + throw new AnchorNotFoundException(identifier.getAnchor().get()); + } + return id.get(); + } +} diff --git a/src/main/java/com/sitepark/ies/userrepository/core/usecase/GetUser.java b/src/main/java/com/sitepark/ies/userrepository/core/usecase/GetUser.java index 071fca2..61baec5 100644 --- a/src/main/java/com/sitepark/ies/userrepository/core/usecase/GetUser.java +++ b/src/main/java/com/sitepark/ies/userrepository/core/usecase/GetUser.java @@ -4,10 +4,12 @@ import javax.inject.Inject; +import com.sitepark.ies.userrepository.core.domain.entity.Identifier; import com.sitepark.ies.userrepository.core.domain.entity.Role; import com.sitepark.ies.userrepository.core.domain.entity.User; import com.sitepark.ies.userrepository.core.domain.exception.AccessDeniedException; import com.sitepark.ies.userrepository.core.domain.exception.UserNotFoundException; +import com.sitepark.ies.userrepository.core.domain.service.IdentifierResolver; import com.sitepark.ies.userrepository.core.port.AccessControl; import com.sitepark.ies.userrepository.core.port.RoleAssigner; import com.sitepark.ies.userrepository.core.port.UserRepository; @@ -16,6 +18,8 @@ public final class GetUser { private final UserRepository repository; + private final IdentifierResolver identifierResolver; + private final RoleAssigner roleAssigner; private final AccessControl accessControl; @@ -23,14 +27,18 @@ public final class GetUser { @Inject protected GetUser( UserRepository repository, + IdentifierResolver identifierResolver, RoleAssigner roleAssigner, AccessControl accessControl) { this.repository = repository; + this.identifierResolver = identifierResolver; this.roleAssigner = roleAssigner; this.accessControl = accessControl; } - public User getUser(long id) { + public User getUser(Identifier identifier) { + + long id = this.identifierResolver.resolveIdentifier(identifier); if (!this.accessControl.isUserReadable(id)) { throw new AccessDeniedException("Not allowed to reat user " + id); diff --git a/src/main/java/com/sitepark/ies/userrepository/core/usecase/PurgeUser.java b/src/main/java/com/sitepark/ies/userrepository/core/usecase/PurgeUser.java index a604812..d11f4a4 100644 --- a/src/main/java/com/sitepark/ies/userrepository/core/usecase/PurgeUser.java +++ b/src/main/java/com/sitepark/ies/userrepository/core/usecase/PurgeUser.java @@ -5,7 +5,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import com.sitepark.ies.userrepository.core.domain.entity.Identifier; import com.sitepark.ies.userrepository.core.domain.exception.AccessDeniedException; +import com.sitepark.ies.userrepository.core.domain.service.IdentifierResolver; import com.sitepark.ies.userrepository.core.port.AccessControl; import com.sitepark.ies.userrepository.core.port.AccessTokenRepository; import com.sitepark.ies.userrepository.core.port.ExtensionsNotifier; @@ -15,6 +17,8 @@ public final class PurgeUser { private final UserRepository repository; + private final IdentifierResolver identifierResolver; + private final AccessTokenRepository accessTokenRepository; private final ExtensionsNotifier extensionsNotifier; @@ -26,17 +30,21 @@ public final class PurgeUser { @Inject protected PurgeUser( UserRepository repository, + IdentifierResolver identifierResolver, ExtensionsNotifier extensionsNotifier, AccessControl accessControl, AccessTokenRepository accessTokenRepository) { this.repository = repository; this.extensionsNotifier = extensionsNotifier; + this.identifierResolver = identifierResolver; this.accessControl = accessControl; this.accessTokenRepository = accessTokenRepository; } - public void purgeUser(long id) { + public void purgeUser(Identifier identifier) { + + long id = this.identifierResolver.resolveIdentifier(identifier); if (!this.accessControl.isUserRemovable(id)) { throw new AccessDeniedException("Not allowed to remove user " + id); diff --git a/src/test/java/com/sitepark/ies/userrepository/core/domain/entity/IdentifierTest.java b/src/test/java/com/sitepark/ies/userrepository/core/domain/entity/IdentifierTest.java new file mode 100644 index 0000000..a320fb7 --- /dev/null +++ b/src/test/java/com/sitepark/ies/userrepository/core/domain/entity/IdentifierTest.java @@ -0,0 +1,61 @@ +package com.sitepark.ies.userrepository.core.domain.entity; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Optional; + +import org.junit.jupiter.api.Test; + +class IdentifierTest { + + @Test + void testOfStringToId() { + Identifier identifier = Identifier.ofString("123"); + assertEquals(Optional.of(123L), identifier.getId(), "id exprected"); + } + + @Test + void testOfStringToAnchor() { + Identifier identifier = Identifier.ofString("abc"); + Anchor anchor = Anchor.ofString("abc"); + assertEquals(Optional.of(anchor), identifier.getAnchor(), "anchor exprected"); + } + + @Test + void testOfStringWithLongString() { + Identifier identifier = Identifier.ofString("abcdefghijklmnopqrstuvwxyz"); + Anchor anchor = Anchor.ofString("abcdefghijklmnopqrstuvwxyz"); + assertEquals(Optional.of(anchor), identifier.getAnchor(), "anchor exprected"); + } + + @Test + void testOfStringWithDot() { + Identifier identifier = Identifier.ofString("123.b"); + Anchor anchor = Anchor.ofString("123.b"); + assertEquals(Optional.of(anchor), identifier.getAnchor(), "anchor exprected"); + } + + @Test + void testOfId() { + Identifier identifier = Identifier.ofId(123L); + assertEquals(Optional.of(123L), identifier.getId(), "id exprected"); + } + + @Test + void testOfAnchor() { + Anchor anchor = Anchor.ofString("abc"); + Identifier identifier = Identifier.ofAnchor(anchor); + assertEquals( + Optional.of(Anchor.ofString("abc")), + identifier.getAnchor(), + "anchor exprected"); + } + + @Test + void testOfAnchorWithNull() { + assertThrows(NullPointerException.class, () -> { + Identifier.ofAnchor(null); + }); + } +} diff --git a/src/test/java/com/sitepark/ies/userrepository/core/domain/service/IdentifierResolverTest.java b/src/test/java/com/sitepark/ies/userrepository/core/domain/service/IdentifierResolverTest.java new file mode 100644 index 0000000..42de323 --- /dev/null +++ b/src/test/java/com/sitepark/ies/userrepository/core/domain/service/IdentifierResolverTest.java @@ -0,0 +1,59 @@ +package com.sitepark.ies.userrepository.core.domain.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Optional; + +import org.junit.jupiter.api.Test; + +import com.sitepark.ies.userrepository.core.domain.entity.Anchor; +import com.sitepark.ies.userrepository.core.domain.entity.Identifier; +import com.sitepark.ies.userrepository.core.domain.exception.AnchorNotFoundException; +import com.sitepark.ies.userrepository.core.port.UserRepository; + +class IdentifierResolverTest { + + @Test + void testResolveWithId() { + + Identifier identifier = Identifier.ofId(123L); + UserRepository repository = mock(); + IdentifierResolver resolver = new IdentifierResolver(repository); + + long id = resolver.resolveIdentifier(identifier); + + assertEquals(123L, id, "unexpected id"); + } + + @Test + void testResolveWithAnchor() { + + Anchor anchor = Anchor.ofString("abc"); + Identifier identifier = Identifier.ofAnchor(anchor); + UserRepository repository = mock(); + when(repository.resolveAnchor(any())).thenReturn(Optional.of(123L)); + IdentifierResolver resolver = new IdentifierResolver(repository); + + long id = resolver.resolveIdentifier(identifier); + + assertEquals(123L, id, "unexpected id"); + } + + @Test + void testResolveWithAnchorNotFound() { + + Anchor anchor = Anchor.ofString("abc"); + Identifier identifier = Identifier.ofAnchor(anchor); + UserRepository repository = mock(); + when(repository.resolveAnchor(any())).thenReturn(Optional.empty()); + IdentifierResolver resolver = new IdentifierResolver(repository); + + assertThrows(AnchorNotFoundException.class, () -> { + resolver.resolveIdentifier(identifier); + }); + } +} diff --git a/src/test/java/com/sitepark/ies/userrepository/core/usecase/GetUserTest.java b/src/test/java/com/sitepark/ies/userrepository/core/usecase/GetUserTest.java index c6f83a4..21e13df 100644 --- a/src/test/java/com/sitepark/ies/userrepository/core/usecase/GetUserTest.java +++ b/src/test/java/com/sitepark/ies/userrepository/core/usecase/GetUserTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -12,11 +13,13 @@ import org.junit.jupiter.api.Test; +import com.sitepark.ies.userrepository.core.domain.entity.Identifier; import com.sitepark.ies.userrepository.core.domain.entity.User; import com.sitepark.ies.userrepository.core.domain.entity.role.Ref; import com.sitepark.ies.userrepository.core.domain.entity.role.UserLevelRoles; import com.sitepark.ies.userrepository.core.domain.exception.AccessDeniedException; import com.sitepark.ies.userrepository.core.domain.exception.UserNotFoundException; +import com.sitepark.ies.userrepository.core.domain.service.IdentifierResolver; import com.sitepark.ies.userrepository.core.port.AccessControl; import com.sitepark.ies.userrepository.core.port.RoleAssigner; import com.sitepark.ies.userrepository.core.port.UserRepository; @@ -24,20 +27,44 @@ class GetUserTest { @Test - void testAccessDeniedGet() { + void testAccessDeniedGetWithId() { UserRepository userRepository = mock(); + IdentifierResolver identifierResolver = mock(); + when(identifierResolver.resolveIdentifier(any())).thenReturn(123L); RoleAssigner roleAssigner = mock(); AccessControl accessControl = mock(); when(accessControl.isUserReadable(123L)).thenReturn(false); GetUser getUserUseCase = new GetUser( userRepository, + identifierResolver, roleAssigner, accessControl); assertThrows(AccessDeniedException.class, () -> { - getUserUseCase.getUser(123L); + getUserUseCase.getUser(Identifier.ofString("123")); + }); + } + + @Test + void testAccessDeniedGetWithAnchor() { + + UserRepository userRepository = mock(); + IdentifierResolver identifierResolver = mock(); + when(identifierResolver.resolveIdentifier(any())).thenReturn(123L); + RoleAssigner roleAssigner = mock(); + AccessControl accessControl = mock(); + when(accessControl.isUserReadable(123L)).thenReturn(false); + + GetUser getUserUseCase = new GetUser( + userRepository, + identifierResolver, + roleAssigner, + accessControl); + + assertThrows(AccessDeniedException.class, () -> { + getUserUseCase.getUser(Identifier.ofString("abc")); }); } @@ -50,6 +77,8 @@ void testGet() { .login("test") .build(); when(userRepository.get(123L)).thenReturn(Optional.of(storedUser)); + IdentifierResolver identifierResolver = mock(); + when(identifierResolver.resolveIdentifier(any())).thenReturn(123L); RoleAssigner roleAssigner = mock(); when(roleAssigner.getRolesAssignByUser(123L)).thenReturn(Arrays.asList( UserLevelRoles.USER, @@ -60,6 +89,7 @@ void testGet() { GetUser getUserUseCase = new GetUser( userRepository, + identifierResolver, roleAssigner, accessControl); @@ -69,7 +99,7 @@ void testGet() { .roleList(UserLevelRoles.USER, Ref.ofAnchor("role.a")) .build(); - User user = getUserUseCase.getUser(123L); + User user = getUserUseCase.getUser(Identifier.ofString("123")); assertEquals(expectedUser, user, "unexpected user"); } @@ -78,17 +108,20 @@ void testGet() { void testGetUserNotFound() { UserRepository userRepository = mock(); + IdentifierResolver identifierResolver = mock(); + when(identifierResolver.resolveIdentifier(any())).thenReturn(123L); RoleAssigner roleAssigner = mock(); AccessControl accessControl = mock(); when(accessControl.isUserReadable(anyLong())).thenReturn(true); GetUser getUserUseCase = new GetUser( userRepository, + identifierResolver, roleAssigner, accessControl); UserNotFoundException e = assertThrows(UserNotFoundException.class, () -> { - getUserUseCase.getUser(123L); + getUserUseCase.getUser(Identifier.ofString("123")); }); assertEquals(123L, e.getId(), "unexpected user"); assertNotNull(e.getMessage(), "message is null"); diff --git a/src/test/java/com/sitepark/ies/userrepository/core/usecase/PurgeUserTest.java b/src/test/java/com/sitepark/ies/userrepository/core/usecase/PurgeUserTest.java index b63efc0..1e7bcec 100644 --- a/src/test/java/com/sitepark/ies/userrepository/core/usecase/PurgeUserTest.java +++ b/src/test/java/com/sitepark/ies/userrepository/core/usecase/PurgeUserTest.java @@ -1,6 +1,7 @@ package com.sitepark.ies.userrepository.core.usecase; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -8,7 +9,9 @@ import org.junit.jupiter.api.Test; +import com.sitepark.ies.userrepository.core.domain.entity.Identifier; import com.sitepark.ies.userrepository.core.domain.exception.AccessDeniedException; +import com.sitepark.ies.userrepository.core.domain.service.IdentifierResolver; import com.sitepark.ies.userrepository.core.port.AccessControl; import com.sitepark.ies.userrepository.core.port.AccessTokenRepository; import com.sitepark.ies.userrepository.core.port.ExtensionsNotifier; @@ -20,17 +23,19 @@ class PurgeUserTest { void testAccessDenied() { AccessControl accessControl = mock(AccessControl.class); + IdentifierResolver identifierResolver = mock(); AccessTokenRepository accessTokenRepository = mock(AccessTokenRepository.class); ExtensionsNotifier extensionsNotifier = mock(ExtensionsNotifier.class); when(accessControl.isUserRemovable(anyLong())).thenReturn(false); var purgeEntity = new PurgeUser( null, + identifierResolver, extensionsNotifier, accessControl, accessTokenRepository); assertThrows(AccessDeniedException.class, () -> { - purgeEntity.purgeUser(10L); + purgeEntity.purgeUser(Identifier.ofId(10L)); }); } @@ -38,6 +43,8 @@ void testAccessDenied() { @Test void testPurge() { + IdentifierResolver identifierResolver = mock(); + when(identifierResolver.resolveIdentifier(any())).thenReturn(10L); AccessControl accessControl = mock(AccessControl.class); AccessTokenRepository accessTokenRepository = mock(AccessTokenRepository.class); when(accessControl.isUserRemovable(anyLong())).thenReturn(true); @@ -47,10 +54,11 @@ void testPurge() { PurgeUser purgeEntity = new PurgeUser( repository, + identifierResolver, extensionsNotifier, accessControl, accessTokenRepository); - purgeEntity.purgeUser(10L); + purgeEntity.purgeUser(Identifier.ofId(10L)); verify(repository).remove(10L); verify(extensionsNotifier).notifyPurge(10L);