Skip to content
Snippets Groups Projects
Commit dc9e2698 authored by Felix Reichenbach's avatar Felix Reichenbach
Browse files

OZG-7909 save incoming aggregation data in mogno db

parent 832c9678
No related branches found
No related tags found
1 merge request!20Ozg 7909 persist server data
Showing
with 607 additions and 3 deletions
......@@ -51,11 +51,26 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- gRPC -->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
</dependency>
<!-- Own projects -->
<dependency>
<groupId>de.ozgcloud.aggregation</groupId>
<artifactId>aggregation-manager-interface</artifactId>
</dependency>
<!--dev
tools-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</dependency>
</dependencies>
<build>
......
package de.ozgcloud.aggregation.data;
import java.time.ZonedDateTime;
import java.util.Map;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Builder(toBuilder = true)
@Getter
@EqualsAndHashCode
@TypeAlias("AggregationData")
@Document(collection = AggregationData.COLLECTION)
class AggregationData {
static final String COLLECTION = "AggregationData";
@Id
private String id;
private String kommune;
private String status;
private ZonedDateTime eingangDatum;
private String vorgangName;
private Map<String, Object> payload;
}
package de.ozgcloud.aggregation.data;
import org.springframework.beans.factory.annotation.Lookup;
import de.ozgcloud.aggregation.data.AggregationDataServiceGrpc.AggregationDataServiceImplBase;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
class AggregationDataGrpcService extends AggregationDataServiceImplBase {
@GrpcService
abstract class AggregationDataGrpcService extends AggregationDataServiceImplBase {
@Override
public StreamObserver<GrpcSendAggregationDataRequest> sendAggregationData(StreamObserver<GrpcSendAggregationDataResponse> responseObserver) {
// Implement in OZG-7909
return null;
return getGrpcAggregationDataRequestObserver().withResponseObserver(responseObserver);
}
@Lookup
abstract GrpcAggregationDataRequestObserver getGrpcAggregationDataRequestObserver();
}
package de.ozgcloud.aggregation.data;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper(uses = GrpcObjectMapper.class)
interface AggregationDataMapper {
@Mapping(target = "kommune", ignore = true)
AggregationData fromGrpc(GrpcAggregationData grpcAggregationData);
}
package de.ozgcloud.aggregation.data;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
@Repository
interface AggregationDataRepository extends MongoRepository<AggregationData, String>, CustomAggregationDataRepository {
}
package de.ozgcloud.aggregation.data;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
class AggregationDataService {
private final AggregationDataRepository repository;
public void save(AggregationData aggregationData, String mappingName) {
repository.saveInCollection(aggregationData, mappingName);
}
}
package de.ozgcloud.aggregation.data;
interface CustomAggregationDataRepository {
AggregationData saveInCollection(AggregationData aggregationData, String collectionName);
}
package de.ozgcloud.aggregation.data;
import org.springframework.data.mongodb.core.MongoTemplate;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
class CustomAggregationDataRepositoryImpl implements CustomAggregationDataRepository {
private final MongoTemplate mongoTemplate;
@Override
public AggregationData saveInCollection(AggregationData aggregationData, String collectionName) {
return mongoTemplate.save(aggregationData, collectionName);
}
}
package de.ozgcloud.aggregation.data;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import io.grpc.stub.StreamObserver;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Component
@Scope("prototype")
class GrpcAggregationDataRequestObserver implements StreamObserver<GrpcSendAggregationDataRequest> {
private final AggregationDataMapper aggregationDataMapper;
private final AggregationDataService aggregationDataService;
private StreamObserver<GrpcSendAggregationDataResponse> responseObserver;
public GrpcAggregationDataRequestObserver withResponseObserver(StreamObserver<GrpcSendAggregationDataResponse> responseObserver) {
this.responseObserver = responseObserver;
return this;
}
@Override
public void onNext(GrpcSendAggregationDataRequest request) {
request.getAggregationDataList().stream().map(aggregationDataMapper::fromGrpc)
.forEach(data -> aggregationDataService.save(data.toBuilder().kommune(request.getMandant()).build(), request.getName()));
}
@Override
public void onError(Throwable t) {
responseObserver.onError(t);
}
@Override
public void onCompleted() {
responseObserver.onNext(GrpcSendAggregationDataResponse.getDefaultInstance());
responseObserver.onCompleted();
}
}
package de.ozgcloud.aggregation.data;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.mapstruct.Mapper;
@Mapper
interface GrpcObjectMapper {
default Map<String, Object> toMap(GrpcObject object) {
return object.getPropertiesList().stream().map(this::mapProperty).collect(
Collectors.toMap(Entry::getKey, Entry::getValue));
}
default Entry<String, Object> mapProperty(GrpcProperty property) {
return Map.entry(property.getKey(), mapElement(property.getValue()));
}
default Object mapElement(GrpcElement element) {
if (element.hasStringValue()) {
return element.getStringValue();
}
if (element.hasLongValue()) {
return element.getLongValue();
}
if (element.hasDoubleValue()) {
return element.getDoubleValue();
}
if (element.hasBoolValue()) {
return element.getBoolValue();
}
if (element.hasObjectValue()) {
return toMap(element.getObjectValue());
}
if (element.hasListValue()) {
return element.getListValue().getElementsList().stream()
.map(this::mapElement).toList();
}
return null;
}
}
......@@ -2,7 +2,9 @@ package de.ozgcloud.aggregation.mapping;
import de.ozgcloud.aggregation.mapping.AggregationMappingServiceGrpc.AggregationMappingServiceImplBase;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
@GrpcService
class AggregationMappingGrpcService extends AggregationMappingServiceImplBase {
@Override
......
package de.ozgcloud.aggregation.data;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import io.grpc.stub.StreamObserver;
class AggregationDataGrpcServiceTest {
@InjectMocks
@Spy
private AggregationDataGrpcServiceImpl service;
@Mock
private static GrpcAggregationDataRequestObserver grpcAggregationDataRequestObserver;
@Nested
class TestSendAggregationData {
@Mock
private StreamObserver<GrpcSendAggregationDataResponse> responseObserver;
@Mock
private GrpcAggregationDataRequestObserver grpcAggregationDataRequestObserverWithRepsonseObserver;
@BeforeEach
void mock() {
when(grpcAggregationDataRequestObserver.withResponseObserver(any())).thenReturn(grpcAggregationDataRequestObserverWithRepsonseObserver);
}
@Test
void shouldGetGrpcAggregationDataRequestObserver() {
service.sendAggregationData(responseObserver);
verify(service).getGrpcAggregationDataRequestObserver();
}
@Test
void shouldSetResponseObserver() {
service.sendAggregationData(responseObserver);
verify(grpcAggregationDataRequestObserver).withResponseObserver(responseObserver);
}
@Test
void shouldReturnRequestObserverWithResponseObserver() {
var requestObserver = service.sendAggregationData(responseObserver);
assertThat(requestObserver).isSameAs(grpcAggregationDataRequestObserverWithRepsonseObserver);
}
}
static class AggregationDataGrpcServiceImpl extends AggregationDataGrpcService {
@Override
GrpcAggregationDataRequestObserver getGrpcAggregationDataRequestObserver() {
return grpcAggregationDataRequestObserver;
}
}
}
package de.ozgcloud.aggregation.data;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mapstruct.factory.Mappers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
class AggregationDataMapperTest {
@InjectMocks
private final AggregationDataMapper aggregationDataMapper = Mappers.getMapper(AggregationDataMapper.class);
@Mock
private GrpcObjectMapper grpcObjectMapper;
@Nested
class TestFromGrpc {
@Test
void shouldMapHeaderData() {
var aggregationData = aggregationDataMapper.fromGrpc(GrpcAggregationDataTestFactory.create());
assertThat(aggregationData).usingRecursiveComparison()
.ignoringFields("payload", "kommune")
.isEqualTo(AggregationDataTestFactory.create());
}
@Test
void shouldCallGrpcObjectMapper() {
aggregationDataMapper.fromGrpc(GrpcAggregationDataTestFactory.create());
verify(grpcObjectMapper).toMap(GrpcAggregationDataTestFactory.PAYLOAD);
}
@Test
void shouldSetPayload() {
when(grpcObjectMapper.toMap(any())).thenReturn(AggregationDataTestFactory.PAYLOAD);
var aggregationData = aggregationDataMapper.fromGrpc(GrpcAggregationDataTestFactory.create());
assertThat(aggregationData.getPayload()).isEqualTo(AggregationDataTestFactory.PAYLOAD);
}
}
}
package de.ozgcloud.aggregation.data;
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import com.thedeanda.lorem.LoremIpsum;
class AggregationDataServiceTest {
@InjectMocks
private AggregationDataService service;
@Mock
private AggregationDataRepository repository;
@Nested
class TestSave {
@Test
void shouldSaveInRepository() {
var aggregationData = AggregationDataTestFactory.create();
var mappingName = LoremIpsum.getInstance().getWords(1);
service.save(aggregationData, mappingName);
verify(repository).saveInCollection(aggregationData, mappingName);
}
}
}
package de.ozgcloud.aggregation.data;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import com.thedeanda.lorem.LoremIpsum;
public class AggregationDataTestFactory {
public static final String KOMMUNE = LoremIpsum.getInstance().getWords(1);
public static final String ID = GrpcAggregationDataTestFactory.ID;
public static final String STATUS = GrpcAggregationDataTestFactory.STATUS;
public static final ZonedDateTime EIGNANG_DATUM = ZonedDateTime.parse(GrpcAggregationDataTestFactory.EINGANG_DATUM);
public static final String VORGANG_NAME = GrpcAggregationDataTestFactory.VORGANG_NAME;
public static final Map<String, Object> PAYLOAD = Map.of(
GrpcPropertyTestFactory.STRING_KEY, GrpcPropertyTestFactory.STRING_VALUE,
GrpcPropertyTestFactory.LIST_KEY, List.of(GrpcPropertyTestFactory.LIST_VALUE),
GrpcPropertyTestFactory.MAP_KEY, Map.of(GrpcPropertyTestFactory.NESTED_MAP_KEY, GrpcPropertyTestFactory.MAP_VALUE));
public static AggregationData create() {
return createBuilder().build();
}
public static AggregationData.AggregationDataBuilder createBuilder() {
return AggregationData.builder()
.id(ID)
.kommune(KOMMUNE)
.status(STATUS)
.eingangDatum(EIGNANG_DATUM)
.vorgangName(VORGANG_NAME)
.payload(PAYLOAD);
}
}
package de.ozgcloud.aggregation.data;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.data.mongodb.core.MongoTemplate;
import com.thedeanda.lorem.LoremIpsum;
class CustomAggregationDataRepositoryImplTest {
@InjectMocks
private CustomAggregationDataRepositoryImpl repository;
@Mock
private MongoTemplate mongoTemplate;
@Nested
class TestSaveInCollection {
private final AggregationData aggregationData = AggregationDataTestFactory.create();
private final String collectionName = LoremIpsum.getInstance().getWords(1);
@BeforeEach
void mock() {
when(mongoTemplate.save(any(), any())).thenReturn(aggregationData);
}
@Test
void shouldSaveInCollection() {
repository.saveInCollection(aggregationData, collectionName);
verify(mongoTemplate).save(aggregationData, collectionName);
}
@Test
void shouldReturnSavedAggregationData() {
var result = repository.saveInCollection(aggregationData, collectionName);
assertThat(result).isSameAs(aggregationData);
}
}
}
package de.ozgcloud.aggregation.data;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.test.util.ReflectionTestUtils;
import io.grpc.stub.StreamObserver;
class GrpcAggregationDataRequestObserverTest {
@InjectMocks
private GrpcAggregationDataRequestObserver grpcAggregationDataRequestObserver;
@Mock
private AggregationDataMapper aggregationDataMapper;
@Mock
private AggregationDataService aggregationDataService;
@Mock
private StreamObserver<GrpcSendAggregationDataResponse> responseObserver;
@Nested
class TestWithResponseObserver {
@Test
void shouldSetResponseObserver() {
var requestObserver = grpcAggregationDataRequestObserver.withResponseObserver(responseObserver);
assertThat(ReflectionTestUtils.getField(requestObserver, GrpcAggregationDataRequestObserver.class, "responseObserver"))
.isEqualTo(responseObserver);
}
}
@Nested
class TestOnNext {
@BeforeEach
void mock() {
when(aggregationDataMapper.fromGrpc(any())).thenReturn(AggregationDataTestFactory.createBuilder().kommune(null).build());
}
@Test
void shouldCallAggregationDataMapper() {
grpcAggregationDataRequestObserver.onNext(GrpcSendAggregationDataRequestTestFactory.create());
verify(aggregationDataMapper).fromGrpc(GrpcSendAggregationDataRequestTestFactory.AGGREGATION_DATA);
}
@Test
void shouldCallAggregationDataService() {
grpcAggregationDataRequestObserver.onNext(GrpcSendAggregationDataRequestTestFactory.create());
verify(aggregationDataService).save(AggregationDataTestFactory.create(), GrpcSendAggregationDataRequestTestFactory.MAPPING_NAME);
}
}
@Nested
class TestOnError {
@BeforeEach
void init() {
grpcAggregationDataRequestObserver.withResponseObserver(responseObserver);
}
@Test
void shouldCallResponseObserverOnError() {
var error = new RuntimeException("Test error");
grpcAggregationDataRequestObserver.onError(error);
verify(responseObserver).onError(error);
}
}
@Nested
class TestOnCompleted {
@BeforeEach
void init() {
grpcAggregationDataRequestObserver.withResponseObserver(responseObserver);
}
@Test
void shouldCallOnNext() {
grpcAggregationDataRequestObserver.onCompleted();
verify(responseObserver).onNext(GrpcSendAggregationDataResponse.getDefaultInstance());
}
@Test
void shouldCallResponseObserverOnCompleted() {
grpcAggregationDataRequestObserver.onCompleted();
verify(responseObserver).onCompleted();
}
}
}
package de.ozgcloud.aggregation.data;
import java.time.ZonedDateTime;
import java.util.UUID;
import com.thedeanda.lorem.LoremIpsum;
import de.ozgcloud.aggregation.data.GrpcAggregationData.Builder;
public class GrpcAggregationDataTestFactory {
public static final GrpcObject PAYLOAD = GrpcObjectTestFactory.create();
public static final String STATUS = LoremIpsum.getInstance().getWords(1);
public static final String ID = UUID.randomUUID().toString();
public static final String EINGANG_DATUM = ZonedDateTime.now().toString();
public static final String VORGANG_NAME = LoremIpsum.getInstance().getWords(1);
public static GrpcAggregationData create() {
return createBuilder().build();
}
public static Builder createBuilder() {
return GrpcAggregationData.newBuilder()
.setId(ID)
.setStatus(STATUS)
.setEingangDatum(EINGANG_DATUM)
.setVorgangName(VORGANG_NAME)
.setPayload(PAYLOAD);
}
}
package de.ozgcloud.aggregation.data;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mapstruct.factory.Mappers;
class GrpcObjectMapperTest {
private final GrpcObjectMapper grpcObjectMapper = Mappers.getMapper(GrpcObjectMapper.class);
@Nested
class TestToMap {
@Test
void shouldMapGrpcObjectToMap() {
var map = grpcObjectMapper.toMap(GrpcObjectTestFactory.create());
assertThat(map).usingRecursiveComparison().isEqualTo(AggregationDataTestFactory.PAYLOAD);
}
}
}
package de.ozgcloud.aggregation.data;
import de.ozgcloud.aggregation.data.GrpcObject.Builder;
public class GrpcObjectTestFactory {
public static GrpcObject create() {
return createBuilder().build();
}
private static Builder createBuilder() {
return GrpcObject.newBuilder()
.addProperties(GrpcPropertyTestFactory.createStringProperty())
.addProperties(GrpcPropertyTestFactory.createListProperty())
.addProperties(GrpcPropertyTestFactory.creatMapProperty());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment