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

Merge branch 'OZG-7609-fix-config-client' into 'main'

OZG-7609 load configurations from config server

See merge request !12
parents 21422f77 2758985c
Branches
Tags
1 merge request!12OZG-7609 load configurations from config server
Showing with 334 additions and 31 deletions
......@@ -26,14 +26,18 @@ package de.ozgcloud.aggregation;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import de.ozgcloud.aggregation.transformation.AggregationMapping;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;
@ConfigurationProperties(prefix = "ozgcloud.aggregation")
@Configuration
@Getter
@Setter
@Log4j2
public class TransformationProperties {
/*
......
......@@ -28,26 +28,28 @@ import java.util.List;
import lombok.Builder;
import lombok.Getter;
import lombok.Singular;
import lombok.ToString;
@Builder
@Getter
@Builder
@ToString
public class AggregationMapping {
private FormIdentifier formIdentifier;
@Singular
private List<FieldMapping> mappings;
private List<FieldMapping> fieldMappings;
@Builder
@Getter
static class FormIdentifier {
@Builder
public static class FormIdentifier {
private String formEngineName;
private String formId;
}
@Builder
@Getter
static class FieldMapping {
@Builder
public static class FieldMapping {
private String sourcePath;
private String targetPath;
}
......
......@@ -27,15 +27,20 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.schibsted.spt.data.jslt.Expression;
import com.schibsted.spt.data.jslt.Function;
import com.schibsted.spt.data.jslt.filters.DefaultJsonFilter;
import com.schibsted.spt.data.jslt.impl.AbstractNode;
import com.schibsted.spt.data.jslt.impl.ArraySlicer;
import com.schibsted.spt.data.jslt.impl.DotExpression;
import com.schibsted.spt.data.jslt.impl.ExpressionImpl;
import com.schibsted.spt.data.jslt.impl.ExpressionNode;
......@@ -55,8 +60,9 @@ import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class JSLTransformationService implements TransformationService {
private static final LetExpression[] EMPTY_TEMPLATES = new LetExpression[] {};
private static final Map<String, Function> EMPTY_FUNCTIONS = Map.of();
private static final Pattern LIST_ELEMENT_PATTERN = Pattern.compile("(.+)\\[(\\d+)\\]");
static final LetExpression[] EMPTY_TEMPLATES = new LetExpression[] {};
static final Map<String, Function> EMPTY_FUNCTIONS = Map.of();
private final ObjectMapper objectMapper;
private final VorgangMapper vorgangMapper;
......@@ -66,13 +72,13 @@ public class JSLTransformationService implements TransformationService {
return JSLTransformation.builder()
.vorgangMapper(vorgangMapper)
.objectMapper(objectMapper)
.identifier(transformToDotExpression(identifier))
.identifier(transformToAbstractNode(identifier))
.script(createExpression(transformToMap(getFieldMappings(mapping))))
.build();
}
private List<FieldMapping> getFieldMappings(AggregationMapping mapping) {
return Optional.ofNullable(mapping).map(AggregationMapping::getMappings).orElse(Collections.emptyList());
return Optional.ofNullable(mapping).map(AggregationMapping::getFieldMappings).orElse(Collections.emptyList());
}
Map<String, String> transformToMap(List<FieldMapping> mappings) {
......@@ -86,29 +92,46 @@ public class JSLTransformationService implements TransformationService {
return new ExpressionImpl(EMPTY_TEMPLATES, EMPTY_FUNCTIONS, buildObjectExpression(pairExpressions));
}
private ObjectExpression buildObjectExpression(PairExpression... fields) {
Optional<PairExpression> toPairExpression(Map.Entry<String, String> mapping) {
return transformToAbstractNode(mapping.getValue()).map(path -> buildPairExpression(mapping.getKey(), path));
}
ObjectExpression buildObjectExpression(PairExpression... fields) {
return new ObjectExpression(EMPTY_TEMPLATES, fields, null, null, new DefaultJsonFilter());
}
private Optional<PairExpression> toPairExpression(Map.Entry<String, String> mapping) {
return transformToDotExpression(mapping.getValue()).map(path -> buildPairExpression(mapping.getKey(), path));
PairExpression buildPairExpression(String key, ExpressionNode expression) {
return new PairExpression(new LiteralExpression(new TextNode(key), null), expression, null);
}
Optional<DotExpression> transformToDotExpression(String path) {
Optional<AbstractNode> transformToAbstractNode(String path) {
if (path == null) {
return Optional.empty();
} else {
String[] fields = path.split("\\.");
DotExpression current = null;
var fields = path.split("\\.");
AbstractNode current = null;
for (var field : fields) {
var listElementMatcher = LIST_ELEMENT_PATTERN.matcher(field);
if (listElementMatcher.matches()) {
current = getNodeForListElement(current, listElementMatcher);
} else {
current = new DotExpression(field, current, null);
}
return Optional.ofNullable(current);
}
return Optional.of(current);
}
}
private PairExpression buildPairExpression(String key, ExpressionNode expression) {
return new PairExpression(new LiteralExpression(new TextNode(key), null), expression, null);
private AbstractNode getNodeForListElement(AbstractNode current, Matcher arrayMatcher) {
var index = Integer.parseInt(arrayMatcher.group(2));
var name = arrayMatcher.group(1);
return slicer(current, name, index);
}
private ArraySlicer slicer(AbstractNode parent, String field, int index) {
var idx = new IntNode(index);
var node = new DotExpression(field, parent, null);
return new ArraySlicer(new LiteralExpression(idx, null), false, null, node, null);
}
}
logging:
level:
ROOT: WARN
'[de.ozgcloud]': DEBUG,
'[de.ozgcloud]': DEBUG
'[org.springframework.cloud.config]': DEBUG
'[org.springframework.security]': WARN
config: classpath:log4j2-local.xml
......@@ -19,3 +20,4 @@ ozgcloud:
aggregation-manager:
fetching-batch-size: 5
......@@ -36,6 +36,8 @@ grpc:
spring:
config:
import: optional:configserver:${ozgcloud_administration_address}/configserver/
application:
name: OzgCloud_AggregationManager
aggregation-manager:
fetching-batch-size: 100
......
......@@ -39,6 +39,6 @@ public class AggregationMappingTestFactory {
public static AggregationMappingBuilder createBuilder() {
return AggregationMapping.builder()
.formIdentifier(FORM_IDENTIFIER)
.mapping(MAPPING);
.fieldMapping(MAPPING);
}
}
......@@ -46,7 +46,7 @@ public class FieldMappingTestFactory {
}
public static Map<String, String> createAsMap() {
return Map.of(SOURCE_PATH, TARGET_PATH);
return Map.of(TARGET_PATH, SOURCE_PATH);
}
}
......@@ -61,7 +61,8 @@ class JSLTServiceITCase {
.build());
var service = new JSLTransformationService(objectMapper, vorgangMapper);
var transformation = service.load(null, AggregationMappingTestFactory.createBuilder().clearMappings().mappings(fieldMappings).build());
var transformation = service.load(null,
AggregationMappingTestFactory.createBuilder().clearFieldMappings().fieldMappings(fieldMappings).build());
var vorgang = OzgCloudVorgangTestFactory.create();
......
......@@ -39,12 +39,23 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.schibsted.spt.data.jslt.Expression;
import com.schibsted.spt.data.jslt.impl.DotExpression;
import com.schibsted.spt.data.jslt.impl.ExpressionImpl;
import com.schibsted.spt.data.jslt.impl.ObjectExpression;
import com.schibsted.spt.data.jslt.impl.PairExpression;
import com.schibsted.spt.data.jslt.impl.Scope;
import com.thedeanda.lorem.LoremIpsum;
import de.ozgcloud.aggregation.transformation.AggregationMapping.FieldMapping;
import de.ozgcloud.apilib.vorgang.OzgCloudEingangHeaderTestFactory;
import de.ozgcloud.apilib.vorgang.OzgCloudVorgangEingangTestFactory;
import de.ozgcloud.apilib.vorgang.OzgCloudVorgangHeaderTestFactory;
import de.ozgcloud.apilib.vorgang.OzgCloudVorgangTestFactory;
class JSLTransformationServiceTest {
......@@ -73,7 +84,7 @@ class JSLTransformationServiceTest {
void mock() {
doReturn(mapping).when(service).transformToMap(any());
doReturn(expression).when(service).createExpression(any());
doReturn(identifierExpression).when(service).transformToDotExpression(any());
doReturn(identifierExpression).when(service).transformToAbstractNode(any());
}
@Test
......@@ -115,7 +126,7 @@ class JSLTransformationServiceTest {
void mock() {
doReturn(mapping).when(service).transformToMap(any());
doReturn(expression).when(service).createExpression(any());
doReturn(identifierExpression).when(service).transformToDotExpression(any());
doReturn(identifierExpression).when(service).transformToAbstractNode(any());
}
@Test
......@@ -170,4 +181,262 @@ class JSLTransformationServiceTest {
assertThat(map).containsExactlyInAnyOrderEntriesOf(expectedMap);
}
}
@Nested
class TestCreateExpression {
@Test
void shouldCallToPairExpression() {
createExpression();
verify(service).toPairExpression(Map.entry(FieldMappingTestFactory.TARGET_PATH, FieldMappingTestFactory.SOURCE_PATH));
}
@Test
void shouldMapProperty() {
var expression = service.createExpression(Map.of("target", "vorgangName"));
var mapped = expression.apply(getVorgangTree());
assertThat(mapped).hasToString("{\"target\":\"%s\"}".formatted(OzgCloudVorgangTestFactory.VORGANG_NAME));
}
private JsonNode getVorgangTree() {
var mapper = new ObjectMapper().registerModules(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(SerializationFeature.WRITE_DATES_WITH_ZONE_ID, true);
var vorgangTree = mapper.valueToTree(OzgCloudVorgangTestFactory.create());
return vorgangTree;
}
@Nested
class TestOnPairExpressionPresent {
@Mock
private PairExpression pairExpression;
@Mock
private ObjectExpression objectExpression;
@BeforeEach
void mock() {
doReturn(Optional.of(pairExpression)).when(service).toPairExpression(any());
doReturn(objectExpression).when(service).buildObjectExpression(any());
}
@Test
void shouldBuildObjectExpression() {
createExpression();
verify(service).buildObjectExpression(new PairExpression[] { pairExpression });
}
@Test
void shouldReturnObjectExpression() {
var expression = createExpression();
assertThat(expression).usingRecursiveComparison().isEqualTo(
new ExpressionImpl(JSLTransformationService.EMPTY_TEMPLATES, JSLTransformationService.EMPTY_FUNCTIONS, objectExpression));
}
}
@Nested
class TestOnPairExpressionNotPresent {
@Mock
private ObjectExpression objectExpression;
@BeforeEach
void mock() {
doReturn(Optional.empty()).when(service).toPairExpression(any());
doReturn(objectExpression).when(service).buildObjectExpression();
}
@Test
void shouldBuildObjectExpression() {
createExpression();
verify(service).buildObjectExpression();
}
@Test
void shouldReturnObjectExpression() {
var expression = createExpression();
expression.apply(null);
assertThat(expression).usingRecursiveComparison().isEqualTo(
new ExpressionImpl(JSLTransformationService.EMPTY_TEMPLATES, JSLTransformationService.EMPTY_FUNCTIONS, objectExpression));
}
}
private Expression createExpression() {
return service.createExpression(FieldMappingTestFactory.createAsMap());
}
}
@Nested
class TestToPairExpression {
@Test
void shouldMapProperty() {
var expression = service.transformToAbstractNode("vorgangName");
var mapped = expression.get().apply(Scope.getRoot(2), getVorgangTree());
assertThat(mapped).hasToString("\"%s\"".formatted(OzgCloudVorgangTestFactory.VORGANG_NAME));
}
@Test
void shouldMapNestedProperty() {
var expression = service.transformToAbstractNode("header.aktenzeichen");
var mapped = expression.get().apply(Scope.getRoot(2), getVorgangTree());
assertThat(mapped).hasToString("\"%s\"".formatted(OzgCloudVorgangHeaderTestFactory.AKTENZEICHEN));
}
@Test
void shouldMapListElement() {
var expression = service.transformToAbstractNode("eingangs[0]");
var mapped = expression.get().apply(Scope.getRoot(2), getVorgangTree());
assertThat(mapped).hasToString(getEingangTree().toString());
}
@Test
void shouldMapListElementProperty() {
var expression = service.transformToAbstractNode("eingangs[0].header.formEngineName");
var mapped = expression.get().apply(Scope.getRoot(2), getVorgangTree());
assertThat(mapped).hasToString("\"%s\"".formatted(OzgCloudEingangHeaderTestFactory.FORM_ENGINE_NAME));
}
private JsonNode getVorgangTree() {
var mapper = new ObjectMapper().registerModules(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(SerializationFeature.WRITE_DATES_WITH_ZONE_ID, true);
return mapper.valueToTree(OzgCloudVorgangTestFactory.create());
}
private JsonNode getEingangTree() {
var mapper = new ObjectMapper().registerModules(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(SerializationFeature.WRITE_DATES_WITH_ZONE_ID, true);
return mapper.valueToTree(OzgCloudVorgangEingangTestFactory.create());
}
@Nested
class TestOnDotExpressionPresent {
@Mock
private DotExpression dotExpression;
@Mock
private PairExpression pairExpression;
@BeforeEach
void mock() {
doReturn(Optional.of(dotExpression)).when(service).transformToAbstractNode(any());
doReturn(pairExpression).when(service).buildPairExpression(any(), any());
}
@Test
void shouldBuildPairExpression() {
toPairExpression();
verify(service).buildPairExpression(FieldMappingTestFactory.SOURCE_PATH,
dotExpression);
}
@Test
void shouldReturnPairExpression() {
var returnedPairExpression = toPairExpression();
assertThat(returnedPairExpression).contains(pairExpression);
}
}
@Nested
class TestOnDotExpressionNotPresent {
@BeforeEach
void mock() {
doReturn(Optional.empty()).when(service).transformToAbstractNode(any());
}
@Test
void shouldReturnEmpty() {
var returnedPairExpression = toPairExpression();
assertThat(returnedPairExpression).isEmpty();
}
}
private Optional<PairExpression> toPairExpression() {
return service.toPairExpression(Map.entry(FieldMappingTestFactory.SOURCE_PATH,
FieldMappingTestFactory.TARGET_PATH));
}
}
@Nested
class TestTransformToAbstractNode {
@Test
void shouldReturnEmptyOnNullPath() {
var dotExpression = service.transformToAbstractNode(null);
assertThat(dotExpression).isEmpty();
}
@Test
void shouldMapProperty() {
var expression = service.transformToAbstractNode("vorgangName");
var mapped = expression.get().apply(Scope.getRoot(2), getVorgangTree());
assertThat(mapped).hasToString("\"%s\"".formatted(OzgCloudVorgangTestFactory.VORGANG_NAME));
}
@Test
void shouldMapNestedProperty() {
var expression = service.transformToAbstractNode("header.aktenzeichen");
var mapped = expression.get().apply(Scope.getRoot(2), getVorgangTree());
assertThat(mapped).hasToString("\"%s\"".formatted(OzgCloudVorgangHeaderTestFactory.AKTENZEICHEN));
}
@Test
void shouldMapListElement() {
var expression = service.transformToAbstractNode("eingangs[0]");
var mapped = expression.get().apply(Scope.getRoot(2), getVorgangTree());
assertThat(mapped).hasToString(getEingangTree().toString());
}
@Test
void shouldMapListElementProperty() {
var expression = service.transformToAbstractNode("eingangs[0].header.formEngineName");
var mapped = expression.get().apply(Scope.getRoot(2), getVorgangTree());
assertThat(mapped).hasToString("\"%s\"".formatted(OzgCloudEingangHeaderTestFactory.FORM_ENGINE_NAME));
}
private JsonNode getVorgangTree() {
var mapper = new ObjectMapper().registerModules(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(SerializationFeature.WRITE_DATES_WITH_ZONE_ID, true);
return mapper.valueToTree(OzgCloudVorgangTestFactory.create());
}
private JsonNode getEingangTree() {
var mapper = new ObjectMapper().registerModules(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(SerializationFeature.WRITE_DATES_WITH_ZONE_ID, true);
return mapper.valueToTree(OzgCloudVorgangEingangTestFactory.create());
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment