diff --git a/TODO b/TODO new file mode 100644 index 0000000000000000000000000000000000000000..06a068d15d1140f5fae1796abe68851e38cb44c4 --- /dev/null +++ b/TODO @@ -0,0 +1,7 @@ +- naming: OrganisationsEinheit, OrganisationEinheit, Organisationseinheit +- package structure to distinguish http api, internal service grpc communication and domain, service, repository layers +- Console logs switch to regular line by line instead of JSON entries. For local development only. +- User managers needs to know it external hostname/address used by clients in order to put it in HAL response. So that the further REST calls are made to proper URI. + -> either frontend sends the URL in request + -> or find out something else +- package sync service -> user service Verletzung sync service soll kein user service nutzten \ No newline at end of file diff --git a/user-manager-interface/src/main/proto/organisationseinheit.model.proto b/user-manager-interface/src/main/proto/organisationseinheit.model.proto new file mode 100644 index 0000000000000000000000000000000000000000..6e0dbc0a111747862e8715a3af2c6dd07eca1ad4 --- /dev/null +++ b/user-manager-interface/src/main/proto/organisationseinheit.model.proto @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +syntax = "proto3"; + +package de.itvsh.kop.user.grpc; + +option java_multiple_files = true; +option java_package = "de.itvsh.kop.user.organisationseinheit"; +option java_outer_classname = "OrganisationsEinheitModelProto"; + +message GrpcOrganisationsEinheit { + string organisationsEinheitId = 1; +} \ No newline at end of file diff --git a/user-manager-interface/src/main/proto/organisationseinheit.proto b/user-manager-interface/src/main/proto/organisationseinheit.proto new file mode 100644 index 0000000000000000000000000000000000000000..450aee5fb20cf0201c6e8c381fa47bcae8cc91b1 --- /dev/null +++ b/user-manager-interface/src/main/proto/organisationseinheit.proto @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +syntax = "proto3"; +import "google/protobuf/empty.proto"; +import "organisationseinheit.model.proto"; + +package de.itvsh.kop.user.grpc; + +option java_multiple_files = true; +option java_package = "de.itvsh.kop.user.grpc.organisationseinheit"; +option java_outer_classname = "OrganisationsEinheitProto"; + +service OrganisationsEinheitService { + rpc getSupportedOrganisationsEinheiten(google.protobuf.Empty) returns (GrpcGetSupportedOrganisationsEinheitenResponse); +} + +message GrpcGetSupportedOrganisationsEinheitenResponse { + repeated GrpcOrganisationsEinheit organisationseinheiten = 1; +} \ No newline at end of file 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 4db774a6227542cf4c154275b8416ba02f4bd26c..bb34c97970495956b3cf204a5fcb0141ff156018 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 @@ -25,6 +25,7 @@ package de.itvsh.kop.user; import static de.itvsh.kop.user.User.*; +import java.util.Collection; import java.util.Optional; import java.util.stream.Stream; @@ -32,6 +33,9 @@ import javax.enterprise.context.ApplicationScoped; import org.bson.types.ObjectId; +import com.google.common.collect.Lists; +import com.mongodb.client.model.Filters; + import de.itvsh.kop.common.logging.KopLogging; import de.itvsh.kop.user.common.errorhandling.ResourceNotFoundException; import io.quarkus.mongodb.panache.PanacheMongoRepository; @@ -85,4 +89,8 @@ class UserRepository implements PanacheMongoRepository<User> { return findByIdOptional(user.getId()) .orElse(findByExternalId(user.getExternalId()).orElseThrow(() -> new ResourceNotFoundException(User.class, user.getExternalId()))); } + + public Collection<String> findAllOrganisationsEinheitIds() { + return Lists.newArrayList(mongoCollection().distinct(User.ORGANISATIONS_EINHEIT_IDS_FIELD, Filters.eq(DELETED_FIELD, false), String.class)); + } } \ No newline at end of file diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/UserService.java b/user-manager-server/src/main/java/de/itvsh/kop/user/UserService.java index 29faba06212925767f2281d5a45d33a7c7ef245a..1f5b158cb9a03b7f2646ae0e4c847f4a468835f2 100644 --- a/user-manager-server/src/main/java/de/itvsh/kop/user/UserService.java +++ b/user-manager-server/src/main/java/de/itvsh/kop/user/UserService.java @@ -23,6 +23,7 @@ */ package de.itvsh.kop.user; +import java.util.Collection; import java.util.Optional; import java.util.stream.Stream; @@ -92,4 +93,8 @@ public class UserService { public User findByExternalId(String id) { return repository.findByExternalId(id).orElseThrow(() -> new ResourceNotFoundException(User.class, id)); } + + public Collection<String> findAllOrganisationsEinheitIds() { + return repository.findAllOrganisationsEinheitIds(); + } } \ No newline at end of file diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/organisationseinheit/OrganisationsEinheitGrpcService.java b/user-manager-server/src/main/java/de/itvsh/kop/user/organisationseinheit/OrganisationsEinheitGrpcService.java new file mode 100644 index 0000000000000000000000000000000000000000..0e7e0ee22e3b3f77759262eb18ab991db36378ed --- /dev/null +++ b/user-manager-server/src/main/java/de/itvsh/kop/user/organisationseinheit/OrganisationsEinheitGrpcService.java @@ -0,0 +1,33 @@ +package de.itvsh.kop.user.organisationseinheit; + +import java.util.Collection; + +import javax.inject.Inject; + +import com.google.protobuf.Empty; + +import de.itvsh.kop.user.UserService; +import de.itvsh.kop.user.grpc.organisationseinheit.GrpcGetSupportedOrganisationsEinheitenResponse; +import de.itvsh.kop.user.grpc.organisationseinheit.OrganisationsEinheitServiceGrpc; +import io.grpc.stub.StreamObserver; + +public class OrganisationsEinheitGrpcService extends OrganisationsEinheitServiceGrpc.OrganisationsEinheitServiceImplBase { + + @Inject + private UserService userService; + + @Override + public void getSupportedOrganisationsEinheiten(Empty request, StreamObserver<GrpcGetSupportedOrganisationsEinheitenResponse> responseObserver) { + var organisationsEinheitIds = userService.findAllOrganisationsEinheitIds(); + var response = GrpcGetSupportedOrganisationsEinheitenResponse.newBuilder() + .addAllOrganisationseinheiten(mapToGrpc(organisationsEinheitIds)) + .build(); + + responseObserver.onNext(response); + responseObserver.onCompleted(); + } + + private Collection<GrpcOrganisationsEinheit> mapToGrpc(Collection<String> organisationsEinheitIds) { + return organisationsEinheitIds.stream().map(id -> GrpcOrganisationsEinheit.newBuilder().setOrganisationsEinheitId(id).build()).toList(); + } +} 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 3189e0c7f33edf85fad4f807da3d6eddc5291272..560504e7e47df21b6e9a5f8054a69120f06c7123 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 @@ -38,6 +38,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import com.google.common.collect.Lists; import com.thedeanda.lorem.LoremIpsum; import de.itvsh.kop.user.common.MongoDbTestProfile; @@ -322,4 +323,22 @@ class UserRepositoryITCase { assertThat(user).usingRecursiveComparison().ignoringFields("id").isEqualTo(UserTestFactory.create()); } } + + @Nested + @DisplayName("Test finding all Organisationseinheiten IDs") + class TestFindAllOrganisationsEinheitIds { + @Test + void shouldFindAll() { + repository.persist(UserTestFactory.createBuilder().id(new ObjectId()).organisationsEinheitId("1").build()); + repository.persist(UserTestFactory.createBuilder().id(new ObjectId()).organisationsEinheitIds(Lists.newArrayList("A", "1", "B")).build()); + repository.persist(UserTestFactory.createBuilder().id(new ObjectId()).organisationsEinheitIds(Lists.newArrayList("A", "1", "B")).build()); + repository.persist(UserTestFactory.createBuilder().id(new ObjectId()).organisationsEinheitId("1").build()); + repository.persist(UserTestFactory.createBuilder().id(new ObjectId()).deleted(true).organisationsEinheitId("ABC").build()); + + var result = repository.findAllOrganisationsEinheitIds(); + + assertThat(result).hasSize(4); + assertThat(result).containsExactlyInAnyOrder("1", "A", "B", UserTestFactory.ORGANISTATIONSEINHEITEN_ID); + } + } } \ No newline at end of file diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/UserRepositoryTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/UserRepositoryTest.java index 94b1bc5594d8e3ba20313df5e6402327e6a3e975..efa152d1b7d5cad4905b97046df0aa1f62c745c6 100644 --- a/user-manager-server/src/test/java/de/itvsh/kop/user/UserRepositoryTest.java +++ b/user-manager-server/src/test/java/de/itvsh/kop/user/UserRepositoryTest.java @@ -32,6 +32,7 @@ import java.util.List; import java.util.Optional; import org.assertj.core.api.Condition; +import org.bson.conversions.Bson; import org.bson.types.ObjectId; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -40,6 +41,11 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Spy; +import com.mongodb.client.DistinctIterable; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.Filters; + import de.itvsh.kop.user.common.errorhandling.ResourceNotFoundException; import io.quarkus.mongodb.panache.PanacheQuery; import io.quarkus.mongodb.panache.common.PanacheUpdate; @@ -180,4 +186,25 @@ class UserRepositoryTest { assertThatExceptionOfType(ResourceNotFoundException.class).isThrownBy(() -> userRepository.refresh(UserTestFactory.create())); // NOSONAR } } + + @DisplayName("Test find Organisationseinheiten Ids") + @Nested + class TestFindOrganisationsEinheitIds { + + MongoCollection mongoCollection = mock(MongoCollection.class); + MongoCursor mongoCursor = mock(MongoCursor.class); + + @Test + void shouldFindAllOrganisationsEinheitIds() { + var iterable = mock(DistinctIterable.class); + when(iterable.iterator()).thenReturn(mongoCursor); + doReturn(mongoCollection).when(userRepository).mongoCollection(); + when(mongoCursor.next()).thenReturn(""); + when(mongoCollection.distinct(anyString(), any(Bson.class), any())).thenReturn(iterable); + + userRepository.findAllOrganisationsEinheitIds(); + + verify(mongoCollection).distinct(User.ORGANISATIONS_EINHEIT_IDS_FIELD, Filters.eq(User.DELETED_FIELD, false), String.class); + } + } } diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/UserServiceTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/UserServiceTest.java index 43235c6e126312539c0f9a7e0a993cfe3c68004d..f58b9c9e95c62877068bdf11e5c9e0fd4492e30e 100644 --- a/user-manager-server/src/test/java/de/itvsh/kop/user/UserServiceTest.java +++ b/user-manager-server/src/test/java/de/itvsh/kop/user/UserServiceTest.java @@ -204,4 +204,16 @@ class UserServiceTest { verify(repository).findUsers(SEARCH_QUERY, true, LIMIT); } } + + @DisplayName("Get Organisationseinheiten") + @Nested + class TestGetOrganisationsEinheiten { + + @Test + void shouldGetAllOrganisationsEinheitenForAllUsers() { + service.findAllOrganisationsEinheitIds(); + + verify(repository).findAllOrganisationsEinheitIds(); + } + } } \ No newline at end of file diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/organisationseinheit/OrganisationseinheitGrpcServiceTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/organisationseinheit/OrganisationseinheitGrpcServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ce249df23f05fde037929d9e34d044be63123721 --- /dev/null +++ b/user-manager-server/src/test/java/de/itvsh/kop/user/organisationseinheit/OrganisationseinheitGrpcServiceTest.java @@ -0,0 +1,41 @@ +package de.itvsh.kop.user.organisationseinheit; + +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import com.google.protobuf.Empty; + +import de.itvsh.kop.user.UserService; +import de.itvsh.kop.user.grpc.organisationseinheit.GrpcGetSupportedOrganisationsEinheitenResponse; +import io.grpc.stub.StreamObserver; + +public class OrganisationseinheitGrpcServiceTest { + @InjectMocks + OrganisationsEinheitGrpcService organisationsEinheitGrpcService; + + @Mock + UserService userService; + + @Nested + @DisplayName("Get supported Organisationseinheiten") + class GetSupportedOrganisationsEinheiten { + + @Mock + private StreamObserver<GrpcGetSupportedOrganisationsEinheitenResponse> streamObserver; + + @Test + void shouldGetOrganisationsEinheiten() { + organisationsEinheitGrpcService.getSupportedOrganisationsEinheiten( + Empty.getDefaultInstance(), + streamObserver); + + verify(userService).findAllOrganisationsEinheitIds(); + } + } + +}