Skip to content

Commit ac9bc48

Browse files
committed
fix(spring-yaml-properties): preserve order of an existing property when updating its value
See #10909
1 parent be0077d commit ac9bc48

File tree

3 files changed

+64
-29
lines changed

3 files changed

+64
-29
lines changed

src/main/java/tech/jhipster/lite/module/infrastructure/secondary/YamlFileSpringPropertiesHandler.java

+19-29
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,21 @@
11
package tech.jhipster.lite.module.infrastructure.secondary;
22

3-
import static org.yaml.snakeyaml.comments.CommentType.*;
3+
import static org.yaml.snakeyaml.comments.CommentType.BLOCK;
44

5-
import java.io.File;
6-
import java.io.FileReader;
7-
import java.io.FileWriter;
8-
import java.io.IOException;
9-
import java.io.Writer;
5+
import java.io.*;
106
import java.nio.file.Files;
117
import java.nio.file.Path;
12-
import java.util.ArrayList;
13-
import java.util.Arrays;
14-
import java.util.Collection;
15-
import java.util.List;
16-
import java.util.Map;
17-
import java.util.function.Consumer;
8+
import java.util.*;
189
import java.util.function.Function;
1910
import java.util.function.Predicate;
20-
import org.yaml.snakeyaml.DumperOptions;
11+
import org.yaml.snakeyaml.*;
2112
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
22-
import org.yaml.snakeyaml.LoaderOptions;
23-
import org.yaml.snakeyaml.Yaml;
2413
import org.yaml.snakeyaml.comments.CommentLine;
2514
import org.yaml.snakeyaml.constructor.Constructor;
26-
import org.yaml.snakeyaml.nodes.MappingNode;
27-
import org.yaml.snakeyaml.nodes.Node;
28-
import org.yaml.snakeyaml.nodes.NodeTuple;
29-
import org.yaml.snakeyaml.nodes.ScalarNode;
30-
import org.yaml.snakeyaml.nodes.SequenceNode;
31-
import org.yaml.snakeyaml.nodes.Tag;
15+
import org.yaml.snakeyaml.nodes.*;
3216
import org.yaml.snakeyaml.representer.Representer;
3317
import tech.jhipster.lite.module.domain.Indentation;
34-
import tech.jhipster.lite.module.domain.javaproperties.Comment;
35-
import tech.jhipster.lite.module.domain.javaproperties.PropertyKey;
36-
import tech.jhipster.lite.module.domain.javaproperties.PropertyValue;
18+
import tech.jhipster.lite.module.domain.javaproperties.*;
3719
import tech.jhipster.lite.shared.error.domain.Assert;
3820
import tech.jhipster.lite.shared.error.domain.GeneratorException;
3921
import tech.jhipster.lite.shared.generation.domain.ExcludeFromGeneratedCodeCoverage;
@@ -91,13 +73,21 @@ private void appendPropertyToConfiguration(PropertyKey key, PropertyValue value,
9173

9274
List<NodeTuple> parentValue = parentPropertyNode(key, configuration).getValue();
9375

94-
parentValue.stream().filter(nodeTupleKeyEquals(localKey)).findFirst().ifPresent(removeNode(parentValue));
95-
96-
parentValue.add(new NodeTuple(buildScalarNode(localKey), buildValueNode(value)));
76+
NodeTuple nodeProperty = new NodeTuple(buildScalarNode(localKey), buildValueNode(value));
77+
indexInCollection(parentValue, localKey).ifPresentOrElse(
78+
index -> parentValue.set(index, nodeProperty),
79+
() -> parentValue.add(nodeProperty)
80+
);
9781
}
9882

99-
private Consumer<NodeTuple> removeNode(List<NodeTuple> parentValue) {
100-
return parentValue::remove;
83+
private Optional<Integer> indexInCollection(List<NodeTuple> nodesTuples, String key) {
84+
Predicate<NodeTuple> matchesKey = nodeTupleKeyEquals(key);
85+
for (int i = 0; i < nodesTuples.size(); i++) {
86+
if (matchesKey.test(nodesTuples.get(i))) {
87+
return Optional.of(i);
88+
}
89+
}
90+
return Optional.empty();
10191
}
10292

10393
private Node buildValueNode(PropertyValue value) {

src/test/java/tech/jhipster/lite/module/infrastructure/secondary/YamlFileSpringPropertiesHandlerTest.java

+31
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,37 @@ void shouldAppendPropertyToFileWithProperties() {
6868
);
6969
}
7070

71+
@Test
72+
void shouldKeepExistingOrderWhenReplacingAProperty() {
73+
Path yamlFile = Paths.get(TestFileUtils.tmpDirForTest(), "src/main/resources/application.yml");
74+
loadDefaultProperties(
75+
Paths.get("src/test/resources/projects/project-with-spring-application-yaml/more-complex-application.yml"),
76+
yamlFile
77+
);
78+
YamlFileSpringPropertiesHandler handler = new YamlFileSpringPropertiesHandler(yamlFile, Indentation.DEFAULT);
79+
80+
handler.setValue(propertyKey("spring.datasource.driver-class-name"), propertyValue("org.postgresql.Driver"));
81+
82+
assertThat(content(yamlFile)).contains(
83+
"""
84+
spring:
85+
data:
86+
jpa:
87+
repositories:
88+
bootstrap-mode: deferred
89+
datasource:
90+
driver-class-name: org.postgresql.Driver
91+
hikari:
92+
auto-commit: false
93+
poolName: Hikari
94+
password: ''
95+
type: com.zaxxer.hikari.HikariDataSource
96+
url: jdbc:postgresql://localhost:5432/myapp
97+
username: myapp
98+
"""
99+
);
100+
}
101+
71102
@Test
72103
void shouldRespectProjectIndentation() {
73104
Path yamlFile = Paths.get(TestFileUtils.tmpDirForTest(), "src/main/resources/application.yml");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
spring:
2+
data:
3+
jpa:
4+
repositories:
5+
bootstrap-mode: deferred
6+
datasource:
7+
driver-class-name: org.postgresql.Driver
8+
hikari:
9+
auto-commit: false
10+
poolName: Hikari
11+
password: ''
12+
type: com.zaxxer.hikari.HikariDataSource
13+
url: jdbc:postgresql://localhost:5432/myapp
14+
username: myapp

0 commit comments

Comments
 (0)