Skip to content

Commit dc883dd

Browse files
authored
patch: allow for adding values to arrays (#566)
1 parent be82174 commit dc883dd

File tree

6 files changed

+76
-1
lines changed

6 files changed

+76
-1
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,15 @@ Example:
12251225
}
12261226
```
12271227

1228+
#### `$add`
1229+
1230+
The `$add` operation allows for adding a value to an array.
1231+
1232+
- `$add`
1233+
- `path` : The [JSON path](https://github.com/json-path/JsonPath#readme) to the array
1234+
- `value` : The value to add. If the given value is a string, variable placeholders of the form `${...}` will be replaced from the environment variables and the resulting string can be converted by setting value-type.
1235+
- `value-type` : **optional** [see below](#valuetype)
1236+
12281237
### ValueType
12291238

12301239
One of the following identifiers or can be prefixed with `list of ` to indicate a list of the identified type:

src/main/java/me/itzg/helpers/patch/PatchSetProcessor.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import me.itzg.helpers.env.Interpolator;
1818
import me.itzg.helpers.env.Interpolator.Result;
1919
import me.itzg.helpers.env.MissingVariablesException;
20+
import me.itzg.helpers.patch.model.PatchAddOperation;
2021
import me.itzg.helpers.patch.model.PatchDefinition;
2122
import me.itzg.helpers.patch.model.PatchOperation;
2223
import me.itzg.helpers.patch.model.PatchPutOperation;
@@ -151,6 +152,13 @@ private void applyOps(Map<String, Object> data, List<PatchOperation> ops) throws
151152
"put", putOp + " at " + putOp.getPath(),
152153
obj -> doc.put(putOp.getPath(), putOp.getKey(), obj)
153154
);
155+
} else if (op instanceof PatchAddOperation) {
156+
final PatchAddOperation addOp = (PatchAddOperation) op;
157+
processValueType(
158+
addOp.getValue(), addOp.getValueType(),
159+
"add", addOp + " at " + addOp.getPath(),
160+
obj -> doc.add(addOp.getPath(), obj)
161+
);
154162
}
155163
}
156164
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package me.itzg.helpers.patch.model;
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
6+
import com.fasterxml.jackson.databind.JsonNode;
7+
import lombok.Data;
8+
import lombok.EqualsAndHashCode;
9+
10+
@EqualsAndHashCode(callSuper = true)
11+
@Data
12+
public class PatchAddOperation extends PatchOperation {
13+
@JsonProperty(required = true)
14+
@JsonPropertyDescription("The JSON path to the array to add to.")
15+
String path;
16+
17+
@JsonProperty(required = true)
18+
@JsonPropertyDescription(VALUE_DESCRIPTION)
19+
JsonNode value;
20+
21+
@JsonProperty("value-type")
22+
@JsonPropertyDescription(VALUE_TYPE_DESCRIPTION)
23+
@JsonInclude(JsonInclude.Include.NON_NULL)
24+
String valueType;
25+
}

src/main/java/me/itzg/helpers/patch/model/PatchOperation.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
77
@JsonSubTypes({
88
@JsonSubTypes.Type(value = PatchSetOperation.class, name = "$set"),
9-
@JsonSubTypes.Type(value = PatchPutOperation.class, name = "$put")
9+
@JsonSubTypes.Type(value = PatchPutOperation.class, name = "$put"),
10+
@JsonSubTypes.Type(value = PatchAddOperation.class, name = "$add"),
1011
})
1112
public abstract class PatchOperation {
1213
protected static final String VALUE_DESCRIPTION = "The value to set." +

src/test/java/me/itzg/helpers/patch/PatchSetProcessorTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static java.util.Collections.singletonList;
44
import static org.assertj.core.api.Assertions.assertThat;
55
import static org.mockito.Mockito.*;
6+
import static uk.org.webcompere.modelassert.json.JsonAssertions.assertJson;
67
import static uk.org.webcompere.modelassert.json.JsonAssertions.assertYaml;
78

89
import com.fasterxml.jackson.databind.node.ArrayNode;
@@ -19,6 +20,7 @@
1920
import java.util.stream.Stream;
2021
import me.itzg.helpers.env.EnvironmentVariablesProvider;
2122
import me.itzg.helpers.env.Interpolator;
23+
import me.itzg.helpers.patch.model.PatchAddOperation;
2224
import me.itzg.helpers.patch.model.PatchDefinition;
2325
import me.itzg.helpers.patch.model.PatchPutOperation;
2426
import me.itzg.helpers.patch.model.PatchSet;
@@ -74,6 +76,31 @@ void setInJson(String file, boolean allowComments, @TempDir Path tempDir) throws
7476
);
7577
}
7678

79+
@Test
80+
void addToArray(@TempDir Path tempDir) throws IOException {
81+
final Path src = tempDir.resolve("testing.json");
82+
Files.copy(Paths.get("src/test/resources/patch/testing-with-array.json"), src);
83+
84+
final PatchSetProcessor processor = new PatchSetProcessor(
85+
new Interpolator(environmentVariablesProvider, "CFG_")
86+
);
87+
88+
processor.process(new PatchSet()
89+
.setPatches(singletonList(
90+
new PatchDefinition()
91+
.setFile(src.toString())
92+
.setOps(singletonList(
93+
new PatchAddOperation()
94+
.setPath("$.outer.array")
95+
.setValue(new TextNode("new value"))
96+
))
97+
))
98+
);
99+
100+
assertJson(src)
101+
.at("/outer/array/0").hasValue("new value");
102+
}
103+
77104
@Test
78105
void setInJson5(@TempDir Path tempDir) throws IOException {
79106
final Path src = tempDir.resolve("testing.json5");
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"outer": {
3+
"array": []
4+
}
5+
}

0 commit comments

Comments
 (0)