diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/User.java b/user-manager-server/src/main/java/de/itvsh/kop/user/User.java index 76a9e3e7ef368c8333a5308eb5052b2b16fce9de..2f9a17ac7f91003b55438437e371037d99c14174 100644 --- a/user-manager-server/src/main/java/de/itvsh/kop/user/User.java +++ b/user-manager-server/src/main/java/de/itvsh/kop/user/User.java @@ -57,6 +57,8 @@ public class User { public static final String LAST_SYNC_TIMESTAMP_FIELD = "lastSyncTimestamp"; public static final String LASTNAME_FIELD = "lastName"; public static final String FIRSTNAME_FIELD = "firstName"; + public static final String FULL_NAME_FIELD = "fullName"; + public static final String FULL_NAME_REVERSED_FIELD = "fullNameReversed"; public static final String USERNAME_FIELD = "username"; public static final String ORGANISATIONS_EINHEIT_IDS_FIELD = "organisationsEinheitIds"; public static final String NOTIFICATION_SEND_FOR_FIELD = "userSettings.notificationsSendFor"; @@ -70,6 +72,10 @@ public class User { private String keycloakUserId; private String firstName; private String lastName; + @JsonIgnore + private String fullName; + @JsonIgnore + private String fullNameReversed; private String username; private String email; @JsonIgnore diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/UserRepository.java b/user-manager-server/src/main/java/de/itvsh/kop/user/UserRepository.java index d382098ee5f721d8f112c2e9a18f045a16726dc6..58fe0f1468bfc2edf7b0699c5f775595d50dc9fc 100644 --- a/user-manager-server/src/main/java/de/itvsh/kop/user/UserRepository.java +++ b/user-manager-server/src/main/java/de/itvsh/kop/user/UserRepository.java @@ -50,6 +50,8 @@ class UserRepository implements PanacheMongoRepository<User> { private static final String SEARCH_QUERY = LASTNAME_FIELD + LIKE_OR + FIRSTNAME_FIELD + LIKE_OR + USERNAME_FIELD + LIKE_OR + + FULL_NAME_FIELD + LIKE_OR + + FULL_NAME_REVERSED_FIELD + LIKE_OR + EMAIL_FIELD + " like " + param(PARAM_NAME_SEARCH_BY); private static final String AND_DELETED = " and deleted = " + param(PARAM_NAME_DELETED); diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/UserResourceMapper.java b/user-manager-server/src/main/java/de/itvsh/kop/user/UserResourceMapper.java index 7a0d8d122152aa093ea6325aa69ed7d74918bd34..d15c1c2d10717d9087f5c857481d9b4d2ffda2c0 100644 --- a/user-manager-server/src/main/java/de/itvsh/kop/user/UserResourceMapper.java +++ b/user-manager-server/src/main/java/de/itvsh/kop/user/UserResourceMapper.java @@ -31,6 +31,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Stream; import jakarta.inject.Inject; @@ -58,6 +59,8 @@ public abstract class UserResourceMapper { @Mapping(target = "email", expression = "java(mapEmail(userRes))") @Mapping(target = "firstName", expression = "java(mapFirstName(userRes))") @Mapping(target = "lastName", expression = "java(mapLastName(userRes))") + @Mapping(target = "fullName", expression = "java(mapFullName(userRes))") + @Mapping(target = "fullNameReversed", expression = "java(mapFullNameReversed(userRes))") @Mapping(target = "username", expression = "java(mapUsername(userRes))") @Mapping(target = "externalId", expression = "java(mapId(userRes))") @Mapping(target = "keycloakUserId", expression = "java(mapKeycloakUserId(userRes))") @@ -97,7 +100,6 @@ public abstract class UserResourceMapper { List<String> mapRoles(UserResource userRes) { var roleRepresentation = Optional.ofNullable(userRes.roles().getAll().getClientMappings()) - .filter(Objects::nonNull) .filter(map -> map.containsKey(properties.client())) .map(map -> map.get(properties.client())) .map(ClientMappingsRepresentation::getMappings) @@ -111,7 +113,6 @@ public abstract class UserResourceMapper { return Optional.ofNullable(userRepresentation.getAttributes()) .map(attributes -> attributes.get(properties.ldapIdKey())) - .filter(Objects::nonNull) .map(id -> id.get(0)) .orElseGet(userRepresentation::getId); @@ -136,4 +137,14 @@ public abstract class UserResourceMapper { String mapUsername(UserResource userRes) { return userRes.toRepresentation().getUsername(); } + + String mapFullName(UserResource userRes) { + return String.join(" ", Stream.of(userRes.toRepresentation().getFirstName(), userRes.toRepresentation().getLastName()) + .filter(Objects::nonNull).toArray(String[]::new)); + } + + String mapFullNameReversed(UserResource userRes) { + return String.join(" ", Stream.of(userRes.toRepresentation().getLastName(), userRes.toRepresentation().getFirstName()) + .filter(Objects::nonNull).toArray(String[]::new)); + } } diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/UserRepositoryITCase.java b/user-manager-server/src/test/java/de/itvsh/kop/user/UserRepositoryITCase.java index 13f17a487de01fa5311d6d2ebc96f01099641228..35691484fbd43a53dbf049ed4c2b04e85dd14d73 100644 --- a/user-manager-server/src/test/java/de/itvsh/kop/user/UserRepositoryITCase.java +++ b/user-manager-server/src/test/java/de/itvsh/kop/user/UserRepositoryITCase.java @@ -27,6 +27,7 @@ import static java.util.function.Predicate.not; import static org.assertj.core.api.Assertions.*; import java.time.Instant; +import java.util.Arrays; import java.util.List; import java.util.stream.Stream; @@ -37,6 +38,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import com.thedeanda.lorem.LoremIpsum; @@ -234,6 +238,73 @@ class UserRepositoryITCase { } } + @Nested + class TestFindByFullName { + + @BeforeEach + void init() { + repository.deleteAll(); + } + + @ParameterizedTest + @MethodSource("provideDataForFindUsersByFullName") + void shouldFindUsersByFullName(String query, String[] expectedUsernames) { + Arrays.asList( + UserTestFactory.createBuilder().id(null).username("user1").fullName("Franz Vogel").build(), + UserTestFactory.createBuilder().id(null).username("user2").fullName("Franz von Holzhausen").build(), + UserTestFactory.createBuilder().id(null).username("user3").fullName("Peter Lustig").build() + ).forEach(repository::persist); + + var foundUsernames = repository.findUsers(query, 10).map(User::getUsername); + + assertThat(foundUsernames).containsExactlyInAnyOrder(expectedUsernames); + } + + private static Stream<Arguments> provideDataForFindUsersByFullName() { + return Stream.of( + Arguments.of("Fra", new String[] { "user1", "user2" }), + Arguments.of("Franz", new String[] { "user1", "user2" }), + Arguments.of("Franz v", new String[] { "user1", "user2" }), + Arguments.of("Franz Vo ", new String[0]), + Arguments.of("Franz von", new String[] { "user2" }), + Arguments.of("Franz von Holzhausen", new String[] { "user2" }), + Arguments.of("Franz L", new String[0]), + Arguments.of("Peter V", new String[0]) + ); + } + } + + @Nested + class TestFindByFullNameReversed { + + @BeforeEach + void init() { + repository.deleteAll(); + } + + @ParameterizedTest + @MethodSource("provideDataForFindUsersByFullNameReversed") + void shouldFindUsersByFullNameReversed(String query, String[] expectedUsernames) { + Arrays.asList( + UserTestFactory.createBuilder().id(null).username("user1").fullNameReversed("Langbein Franz").build(), + UserTestFactory.createBuilder().id(null).username("user2").fullNameReversed("Lustig Peter").build(), + UserTestFactory.createBuilder().id(null).username("user3").fullNameReversed("Ilona Nowak").build() + ).forEach(repository::persist); + + var foundUsernames = repository.findUsers(query, 10).map(User::getUsername); + + assertThat(foundUsernames).containsExactlyInAnyOrder(expectedUsernames); + } + + private static Stream<Arguments> provideDataForFindUsersByFullNameReversed() { + return Stream.of( + Arguments.of("L", new String[] { "user1", "user2", "user3" }), + Arguments.of("Lustig", new String[] { "user2" }), + Arguments.of("Lustig Peter", new String[] { "user2" }) + ); + } + } + @Nested class TestFindUsersByDeletedAndOrganisationsEinheitId { diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/UserResourceMapperTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/UserResourceMapperTest.java index f7014b40f301ae964c30204f63ffd1af03c752e3..4817dc2ee222969a4a11307118884f825f6eedcd 100644 --- a/user-manager-server/src/test/java/de/itvsh/kop/user/UserResourceMapperTest.java +++ b/user-manager-server/src/test/java/de/itvsh/kop/user/UserResourceMapperTest.java @@ -175,6 +175,20 @@ class UserResourceMapperTest { assertThat(user.getKeycloakUserId()).isEqualTo(UserRepresentationTestFactory.EXTERNAL_ID_FALLBACK); } + @Test + void shouldMapFullName() { + var user = toKopUser(); + + assertThat(user.getFullName()).isEqualTo(UserRepresentationTestFactory.FIRST_NAME + " " + UserRepresentationTestFactory.LAST_NAME); + } + + @Test + void shouldMapFullNameReversed() { + var user = toKopUser(); + + assertThat(user.getFullNameReversed()).isEqualTo(UserRepresentationTestFactory.LAST_NAME + " " + UserRepresentationTestFactory.FIRST_NAME); + } + private User toKopUser() { return toKopUser(UserResourceTestFactory.create()); } diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/UserTestFactory.java b/user-manager-server/src/test/java/de/itvsh/kop/user/UserTestFactory.java index b3a56bb9506058042a88dba91ed262de80ae58ba..d48a9ceb324cb0ebc215b177e608a0d84d406061 100644 --- a/user-manager-server/src/test/java/de/itvsh/kop/user/UserTestFactory.java +++ b/user-manager-server/src/test/java/de/itvsh/kop/user/UserTestFactory.java @@ -40,6 +40,8 @@ public class UserTestFactory { public static final String FIRST_NAME = LoremIpsum.getInstance().getFirstName(); public static final String LAST_NAME = LoremIpsum.getInstance().getLastName(); + public static final String FULL_NAME = FIRST_NAME + " " + LAST_NAME; + public static final String FULL_NAME_REVERSED = LAST_NAME + " " + FIRST_NAME; public static final String USER_NAME = LoremIpsum.getInstance().getName(); public static final long LAST_SYNC_TIMESTAMP = 1001L; public static final String EMAIL = LoremIpsum.getInstance().getEmail(); @@ -57,6 +59,8 @@ public class UserTestFactory { .keycloakUserId(KEYCLOAK_USER_ID) .firstName(FIRST_NAME) .lastName(LAST_NAME) + .fullName(FULL_NAME) + .fullNameReversed(FULL_NAME_REVERSED) .username(USER_NAME) .lastSyncTimestamp(LAST_SYNC_TIMESTAMP) .email(EMAIL)