Skip to content

Commit

Permalink
Add ApplyJsonPatchStep
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidGregory084 committed Jul 13, 2023
1 parent d245b60 commit a12b7ca
Show file tree
Hide file tree
Showing 12 changed files with 331 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,6 @@ nb-configuration.xml

# MacOS jenv
.java-version

# VS Code
.vscode/
5 changes: 4 additions & 1 deletion lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ def NEEDS_GLUE = [
'ktlint',
'palantirJavaFormat',
'scalafmt',
'sortPom'
'sortPom',
'zjsonPatch',
]
for (glue in NEEDS_GLUE) {
sourceSets.register(glue) {
Expand Down Expand Up @@ -112,6 +113,8 @@ dependencies {
// sortPom
sortPomCompileOnly 'com.github.ekryd.sortpom:sortpom-sorter:3.2.1'
sortPomCompileOnly 'org.slf4j:slf4j-api:2.0.0'
// zjsonPatch
zjsonPatchCompileOnly 'com.flipkart.zjsonpatch:zjsonpatch:0.4.14'
}

// we'll hold the core lib to a high standard
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2023 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.spotless.json;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import com.diffplug.spotless.FormatterFunc;
import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.JarState;
import com.diffplug.spotless.Provisioner;

public class ApplyJsonPatchStep {
// https://mvnrepository.com/artifact/com.flipkart.zjsonpatch/zjsonpatch
static final String MAVEN_COORDINATE = "com.flipkart.zjsonpatch:zjsonpatch";
static final String DEFAULT_VERSION = "0.4.14";

private ApplyJsonPatchStep() {}

public static FormatterStep create(String patchString, Provisioner provisioner) {
return create(DEFAULT_VERSION, patchString, provisioner);
}

public static FormatterStep create(String zjsonPatchVersion, String patchString, Provisioner provisioner) {
Objects.requireNonNull(zjsonPatchVersion, "zjsonPatchVersion cannot be null");
Objects.requireNonNull(patchString, "patchString cannot be null");
Objects.requireNonNull(provisioner, "provisioner cannot be null");
return FormatterStep.createLazy("apply-json-patch", () -> new State(zjsonPatchVersion, patchString, provisioner), State::toFormatter);
}

public static FormatterStep create(List<Map<String, Object>> patch, Provisioner provisioner) {
return create(DEFAULT_VERSION, patch, provisioner);
}

public static FormatterStep create(String zjsonPatchVersion, List<Map<String, Object>> patch, Provisioner provisioner) {
Objects.requireNonNull(zjsonPatchVersion, "zjsonPatchVersion cannot be null");
Objects.requireNonNull(patch, "patch cannot be null");
Objects.requireNonNull(provisioner, "provisioner cannot be null");
return FormatterStep.createLazy("apply-json-patch", () -> new State(zjsonPatchVersion, patch, provisioner), State::toFormatter);
}

static final class State implements Serializable {
private static final long serialVersionUID = 1L;

private final JarState jarState;
private final List<Map<String, Object>> patch;
private final String patchString;

private State(String zjsonPatchVersion, List<Map<String, Object>> patch, Provisioner provisioner) throws IOException {
this.jarState = JarState.from(MAVEN_COORDINATE + ":" + zjsonPatchVersion, provisioner);
this.patch = patch;
this.patchString = null;
}

private State(String zjsonPatchVersion, String patchString, Provisioner provisioner) throws IOException {
this.jarState = JarState.from(MAVEN_COORDINATE + ":" + zjsonPatchVersion, provisioner);
this.patch = null;
this.patchString = patchString;
}

FormatterFunc toFormatter() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> formatterFunc = jarState.getClassLoader().loadClass("com.diffplug.spotless.glue.json.ApplyJsonPatchFormatterFunc");
if (this.patch != null) {
Constructor<?> constructor = formatterFunc.getConstructor(List.class);
return (FormatterFunc) constructor.newInstance(patch);
} else {
Constructor<?> constructor = formatterFunc.getConstructor(String.class);
return (FormatterFunc) constructor.newInstance(patchString);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2023 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.spotless.glue.json;

import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.flipkart.zjsonpatch.JsonPatch;

import com.diffplug.spotless.FormatterFunc;

public class ApplyJsonPatchFormatterFunc implements FormatterFunc {
private final ObjectMapper objectMapper;
private final List<Map<String, Object>> patch;
private final String patchString;

public ApplyJsonPatchFormatterFunc(String patchString) {
this.objectMapper = new ObjectMapper();
this.objectMapper.setDefaultPrettyPrinter(new DefaultPrettyPrinter());
this.patch = null;
this.patchString = patchString;
}

public ApplyJsonPatchFormatterFunc(List<Map<String, Object>> patch) {
this.objectMapper = new ObjectMapper();
this.objectMapper.setDefaultPrettyPrinter(new DefaultPrettyPrinter());
this.patch = patch;
this.patchString = null;
}

@Override
public String apply(String input) throws Exception {
var patchNode = this.patch == null
? objectMapper.readTree(patchString)
: objectMapper.valueToTree(patch);

var inputNode = objectMapper.readTree(input);

var patchedNode = JsonPatch.apply(patchNode, inputNode);

return objectMapper.writeValueAsString(patchedNode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
package com.diffplug.gradle.spotless;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;

import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.json.ApplyJsonPatchStep;
import com.diffplug.spotless.json.JacksonJsonConfig;
import com.diffplug.spotless.json.JacksonJsonStep;
import com.diffplug.spotless.json.JsonSimpleStep;
Expand All @@ -28,6 +31,7 @@
public class JsonExtension extends FormatExtension {
private static final int DEFAULT_INDENTATION = 4;
private static final String DEFAULT_GSON_VERSION = "2.10.1";
private static final String DEFAULT_ZJSONPATCH_VERSION = "0.4.14";
static final String NAME = "json";

@Inject
Expand Down Expand Up @@ -71,6 +75,14 @@ public RomeJson rome(String version) {
return romeConfig;
}

public ApplyJsonPatchConfig applyJsonPatch(List<Map<String, Object>> patch) {
return new ApplyJsonPatchConfig(patch);
}

public ApplyJsonPatchConfig applyJsonPatch(String zjsonPatchVersion, List<Map<String, Object>> patch) {
return new ApplyJsonPatchConfig(zjsonPatchVersion, patch);
}

public class SimpleConfig {
private int indent;

Expand Down Expand Up @@ -191,4 +203,29 @@ protected RomeJson getThis() {
return this;
}
}

public class ApplyJsonPatchConfig {
private String zjsonPatchVersion;
private List<Map<String, Object>> patch;

public ApplyJsonPatchConfig(List<Map<String, Object>> patch) {
this(DEFAULT_ZJSONPATCH_VERSION, patch);
}

public ApplyJsonPatchConfig(String zjsonPatchVersion, List<Map<String, Object>> patch) {
this.zjsonPatchVersion = zjsonPatchVersion;
this.patch = patch;
addStep(createStep());
}

public ApplyJsonPatchConfig version(String zjsonPatchVersion) {
this.zjsonPatchVersion = zjsonPatchVersion;
replaceStep(createStep());
return this;
}

private FormatterStep createStep() {
return ApplyJsonPatchStep.create(zjsonPatchVersion, patch, provisioner());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,44 @@ void jacksonFormattingWithSortingByKeys() throws IOException {
gradleRunner().withArguments("spotlessApply").build();
assertFile("src/main/resources/example.json").sameAsResource("json/sortByKeysAfter_Jackson.json");
}

@Test
void applyJsonPatchReplaceString() throws IOException {
setFile("build.gradle").toLines(
"plugins {",
" id 'java'",
" id 'com.diffplug.spotless'",
"}",
"repositories { mavenCentral() }",
"spotless {",
" json {",
" target 'src/**/*.json'",
" applyJsonPatch([[op: 'replace', path: '/abc', value: 'ghi']])",
" gson()",
" }",
"}");
setFile("src/main/resources/example.json").toResource("json/patchObjectBefore.json");
gradleRunner().withArguments("spotlessApply").build();
assertFile("src/main/resources/example.json").sameAsResource("json/patchObjectAfterReplaceString.json");
}

@Test
void applyJsonPatchReplaceWithObject() throws IOException {
setFile("build.gradle").toLines(
"plugins {",
" id 'java'",
" id 'com.diffplug.spotless'",
"}",
"repositories { mavenCentral() }",
"spotless {",
" json {",
" target 'src/**/*.json'",
" applyJsonPatch([[op: 'replace', path: '/abc', value: [def: 'ghi']]])",
" gson()",
" }",
"}");
setFile("src/main/resources/example.json").toResource("json/patchObjectBefore.json");
gradleRunner().withArguments("spotlessApply").build();
assertFile("src/main/resources/example.json").sameAsResource("json/patchObjectAfterReplaceWithObject.json");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2023 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.spotless.maven.json;

import org.apache.maven.plugins.annotations.Parameter;

import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.json.ApplyJsonPatchStep;
import com.diffplug.spotless.maven.FormatterStepConfig;
import com.diffplug.spotless.maven.FormatterStepFactory;

/**
* A {@link FormatterStepFactory} implementation that corresponds to {@code <applyJsonPatch>...</applyJsonPatch>} configuration element.
*/
public class ApplyJsonPatch implements FormatterStepFactory {
private static final String DEFAULT_ZJSONPATCH_VERSION = "0.4.14";

@Parameter
String zjsonPatchVersion = DEFAULT_ZJSONPATCH_VERSION;

@Parameter
String patch;

@Override
public FormatterStep newFormatterStep(FormatterStepConfig stepConfig) {
return ApplyJsonPatchStep.create(zjsonPatchVersion, patch, stepConfig.getProvisioner());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,8 @@ public void addJackson(JacksonJson jackson) {
public void addRome(RomeJson rome) {
addStepFactory(rome);
}

public void addApplyJsonPatch(ApplyJsonPatch applyJsonPatch) {
addStepFactory(applyJsonPatch);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,23 @@ public void testFormatJson_WithJackson_sortByKeys_spaceAfterKeySeparator() throw
assertFile("json_test.json").sameAsResource("json/sortByKeysAfter_Jackson_spaceAfterKeySeparator.json");
}

@Test
public void testFormatJson_ApplyJsonPatch_replaceString() throws Exception {
writePomWithJsonSteps("<applyJsonPatch><patch>[{\"op\":\"replace\",\"path\":\"/abc\",\"value\":\"ghi\"}]</patch></applyJsonPatch><gson/>");

setFile("json_test.json").toResource("json/patchObjectBefore.json");

mavenRunner().withArguments("spotless:apply").runNoError();
assertFile("json_test.json").sameAsResource("json/patchObjectAfterReplaceString.json");
}

@Test
public void testFormatJson_ApplyJsonPatch_replaceWithObject() throws Exception {
writePomWithJsonSteps("<applyJsonPatch><patch>[{\"op\":\"replace\",\"path\":\"/abc\",\"value\":{\"def\":\"ghi\"}}]</patch></applyJsonPatch><gson/>");

setFile("json_test.json").toResource("json/patchObjectBefore.json");

mavenRunner().withArguments("spotless:apply").runNoError();
assertFile("json_test.json").sameAsResource("json/patchObjectAfterReplaceWithObject.json");
}
}
11 changes: 11 additions & 0 deletions testlib/src/main/resources/json/patchObjectAfterReplaceString.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"abc": "ghi",
"obj": {
"arr": [
1,
2,
3
],
"val": 5
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"abc": {
"def": "ghi"
},
"obj": {
"arr": [
1,
2,
3
],
"val": 5
}
}
Loading

0 comments on commit a12b7ca

Please sign in to comment.