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

OZG-7609 handle target path to list element

parent ba64478c
No related branches found
No related tags found
1 merge request!12OZG-7609 load configurations from config server
...@@ -23,31 +23,32 @@ ...@@ -23,31 +23,32 @@
*/ */
package de.ozgcloud.aggregation.transformation; package de.ozgcloud.aggregation.transformation;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Singular;
import lombok.ToString; import lombok.ToString;
@Getter @Getter
@Setter @Builder
@ToString @ToString
public class AggregationMapping { public class AggregationMapping {
private FormIdentifier formIdentifier; private FormIdentifier formIdentifier;
private List<FieldMapping> fieldMappings = new ArrayList<>(); @Singular
private List<FieldMapping> fieldMappings;
@Getter @Getter
@Setter @Builder
public static class FormIdentifier { public static class FormIdentifier {
private String formEngineName; private String formEngineName;
private String formId; private String formId;
} }
@Getter @Getter
@Setter @Builder
public static class FieldMapping { public static class FieldMapping {
private String sourcePath; private String sourcePath;
private String targetPath; private String targetPath;
......
...@@ -27,15 +27,20 @@ import java.util.Collections; ...@@ -27,15 +27,20 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.TextNode; import com.fasterxml.jackson.databind.node.TextNode;
import com.schibsted.spt.data.jslt.Expression; import com.schibsted.spt.data.jslt.Expression;
import com.schibsted.spt.data.jslt.Function; import com.schibsted.spt.data.jslt.Function;
import com.schibsted.spt.data.jslt.filters.DefaultJsonFilter; 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.DotExpression;
import com.schibsted.spt.data.jslt.impl.ExpressionImpl; import com.schibsted.spt.data.jslt.impl.ExpressionImpl;
import com.schibsted.spt.data.jslt.impl.ExpressionNode; import com.schibsted.spt.data.jslt.impl.ExpressionNode;
...@@ -55,8 +60,9 @@ import lombok.RequiredArgsConstructor; ...@@ -55,8 +60,9 @@ import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor @RequiredArgsConstructor
public class JSLTransformationService implements TransformationService { public class JSLTransformationService implements TransformationService {
private static final LetExpression[] EMPTY_TEMPLATES = new LetExpression[] {}; private static final Pattern LIST_ELEMENT_PATTERN = Pattern.compile("(.+)\\[(\\d+)\\]");
private static final Map<String, Function> EMPTY_FUNCTIONS = Map.of(); static final LetExpression[] EMPTY_TEMPLATES = new LetExpression[] {};
static final Map<String, Function> EMPTY_FUNCTIONS = Map.of();
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
private final VorgangMapper vorgangMapper; private final VorgangMapper vorgangMapper;
...@@ -86,29 +92,47 @@ public class JSLTransformationService implements TransformationService { ...@@ -86,29 +92,47 @@ public class JSLTransformationService implements TransformationService {
return new ExpressionImpl(EMPTY_TEMPLATES, EMPTY_FUNCTIONS, buildObjectExpression(pairExpressions)); return new ExpressionImpl(EMPTY_TEMPLATES, EMPTY_FUNCTIONS, buildObjectExpression(pairExpressions));
} }
private ObjectExpression buildObjectExpression(PairExpression... fields) { Optional<PairExpression> toPairExpression(Map.Entry<String, String> mapping) {
return transformToDotExpression(mapping.getValue()).map(path -> buildPairExpression(mapping.getKey(), path));
}
ObjectExpression buildObjectExpression(PairExpression... fields) {
return new ObjectExpression(EMPTY_TEMPLATES, fields, null, null, new DefaultJsonFilter()); return new ObjectExpression(EMPTY_TEMPLATES, fields, null, null, new DefaultJsonFilter());
} }
private Optional<PairExpression> toPairExpression(Map.Entry<String, String> mapping) { PairExpression buildPairExpression(String key, ExpressionNode expression) {
return transformToDotExpression(mapping.getValue()).map(path -> buildPairExpression(mapping.getKey(), path)); return new PairExpression(new LiteralExpression(new TextNode(key), null), expression, null);
} }
Optional<DotExpression> transformToDotExpression(String path) { Optional<AbstractNode> transformToDotExpression(String path) {
  • Maintainer

    Der name sollte noch angepasst werden, wir geben jetzt keine dorExpression mehr zurück

  • Please register or sign in to reply
if (path == null) { if (path == null) {
return Optional.empty(); return Optional.empty();
} else { } else {
String[] fields = path.split("\\."); var fields = path.split("\\.");
DotExpression current = null; AbstractNode current = null;
for (var field : fields) { 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); current = new DotExpression(field, current, null);
} }
return Optional.ofNullable(current); }
return Optional.of(current);
} }
} }
private PairExpression buildPairExpression(String key, ExpressionNode expression) { private AbstractNode getNodeForListElement(AbstractNode current, Matcher arrayMatcher) {
return new PairExpression(new LiteralExpression(new TextNode(key), null), expression, null); var index = Integer.parseInt(arrayMatcher.group(2));
var name = arrayMatcher.group(1);
current = slicer(current, name, index);
return current;
}
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);
} }
} }
...@@ -39,6 +39,6 @@ public class AggregationMappingTestFactory { ...@@ -39,6 +39,6 @@ public class AggregationMappingTestFactory {
public static AggregationMappingBuilder createBuilder() { public static AggregationMappingBuilder createBuilder() {
return AggregationMapping.builder() return AggregationMapping.builder()
.formIdentifier(FORM_IDENTIFIER) .formIdentifier(FORM_IDENTIFIER)
.mapping(MAPPING); .fieldMapping(MAPPING);
} }
} }
...@@ -46,7 +46,7 @@ public class FieldMappingTestFactory { ...@@ -46,7 +46,7 @@ public class FieldMappingTestFactory {
} }
public static Map<String, String> createAsMap() { 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 { ...@@ -61,7 +61,8 @@ class JSLTServiceITCase {
.build()); .build());
var service = new JSLTransformationService(objectMapper, vorgangMapper); 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(); var vorgang = OzgCloudVorgangTestFactory.create();
......
...@@ -39,12 +39,23 @@ import org.mockito.InjectMocks; ...@@ -39,12 +39,23 @@ import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Spy; import org.mockito.Spy;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; 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.Expression;
import com.schibsted.spt.data.jslt.impl.DotExpression; 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 com.thedeanda.lorem.LoremIpsum;
import de.ozgcloud.aggregation.transformation.AggregationMapping.FieldMapping; 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 { class JSLTransformationServiceTest {
...@@ -170,4 +181,262 @@ class JSLTransformationServiceTest { ...@@ -170,4 +181,262 @@ class JSLTransformationServiceTest {
assertThat(map).containsExactlyInAnyOrderEntriesOf(expectedMap); 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.transformToDotExpression("vorgangName");
var mapped = expression.get().apply(Scope.getRoot(2), getVorgangTree());
assertThat(mapped).hasToString("\"%s\"".formatted(OzgCloudVorgangTestFactory.VORGANG_NAME));
}
@Test
void shouldMapNestedProperty() {
var expression = service.transformToDotExpression("header.aktenzeichen");
var mapped = expression.get().apply(Scope.getRoot(2), getVorgangTree());
assertThat(mapped).hasToString("\"%s\"".formatted(OzgCloudVorgangHeaderTestFactory.AKTENZEICHEN));
}
@Test
void shouldMapListElement() {
var expression = service.transformToDotExpression("eingangs[0]");
var mapped = expression.get().apply(Scope.getRoot(2), getVorgangTree());
assertThat(mapped).hasToString(getEingangTree().toString());
}
@Test
void shouldMapListElementProperty() {
var expression = service.transformToDotExpression("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).transformToDotExpression(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).transformToDotExpression(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 TestTransformToDotExpression {
@Test
void shouldReturnEmptyOnNullPath() {
var dotExpression = service.transformToDotExpression(null);
assertThat(dotExpression).isEmpty();
}
@Test
void shouldMapProperty() {
var expression = service.transformToDotExpression("vorgangName");
var mapped = expression.get().apply(Scope.getRoot(2), getVorgangTree());
assertThat(mapped).hasToString("\"%s\"".formatted(OzgCloudVorgangTestFactory.VORGANG_NAME));
}
@Test
void shouldMapNestedProperty() {
var expression = service.transformToDotExpression("header.aktenzeichen");
var mapped = expression.get().apply(Scope.getRoot(2), getVorgangTree());
assertThat(mapped).hasToString("\"%s\"".formatted(OzgCloudVorgangHeaderTestFactory.AKTENZEICHEN));
}
@Test
void shouldMapListElement() {
var expression = service.transformToDotExpression("eingangs[0]");
var mapped = expression.get().apply(Scope.getRoot(2), getVorgangTree());
assertThat(mapped).hasToString(getEingangTree().toString());
}
@Test
void shouldMapListElementProperty() {
var expression = service.transformToDotExpression("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