From 9f524904d2e8d44b3890b288c3b525fd0b396913 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Wed, 7 Jan 2026 10:56:07 +0100 Subject: [PATCH 01/16] Remove .idea directory from repository. --- .idea/.gitignore | 8 -------- .idea/copilot.data.migration.agent.xml | 6 ------ .idea/copilot.data.migration.ask.xml | 6 ------ .idea/copilot.data.migration.ask2agent.xml | 6 ------ .idea/copilot.data.migration.edit.xml | 6 ------ .idea/gradle.xml | 11 ----------- .idea/misc.xml | 7 ------- .idea/vcs.xml | 6 ------ 8 files changed, 56 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/copilot.data.migration.agent.xml delete mode 100644 .idea/copilot.data.migration.ask.xml delete mode 100644 .idea/copilot.data.migration.ask2agent.xml delete mode 100644 .idea/copilot.data.migration.edit.xml delete mode 100644 .idea/gradle.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/copilot.data.migration.agent.xml b/.idea/copilot.data.migration.agent.xml deleted file mode 100644 index 4ea72a9..0000000 --- a/.idea/copilot.data.migration.agent.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/copilot.data.migration.ask.xml b/.idea/copilot.data.migration.ask.xml deleted file mode 100644 index 7ef04e2..0000000 --- a/.idea/copilot.data.migration.ask.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/copilot.data.migration.ask2agent.xml b/.idea/copilot.data.migration.ask2agent.xml deleted file mode 100644 index 1f2ea11..0000000 --- a/.idea/copilot.data.migration.ask2agent.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/copilot.data.migration.edit.xml b/.idea/copilot.data.migration.edit.xml deleted file mode 100644 index 8648f94..0000000 --- a/.idea/copilot.data.migration.edit.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 9d62f93..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 5cd9a10..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 547cd2904f11fc97fd143365bda2751f0cf279e3 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Wed, 7 Jan 2026 10:58:13 +0100 Subject: [PATCH 02/16] Fix typo in .gitignore. (Ignore .gradle directory again) --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 5b0af70..79b6346 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -cp .gradle +.gradle build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ @@ -37,4 +37,4 @@ bin/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store From 4979d814d73ee833eac9fb8e7a99adf64bf8c429 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Wed, 7 Jan 2026 15:06:03 +0100 Subject: [PATCH 03/16] First drafts for CITATION.cff and codemeta.json. --- CITATION.cff | 22 +++++++++++++ codemeta.json | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 CITATION.cff create mode 100644 codemeta.json diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..c4f184f --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,22 @@ +cff-version: 1.2.0 +message: "If you use this software, please cite it as below." +authors: +- family-names: "Hartmann" + given-names: "Volker" + affiliation: Karlsruhe Institute of Technology + email: volker.hartmann@kit.edu + orcid: "https://orcid.org/0000-0001-6383-5214" +- family-names: "Jejkal" + given-names: "Thomas" + affiliation: Karlsruhe Institute of Technology + email: thomas.jejkal@kit.edu + orcid: "https://orcid.org/0000-0003-2804-688X" +title: "JSONPatch" +type: software +abstract: "JSONPatch is a Java library that implements the JSON Patch standard (RFC 6902) for applying partial modifications to JSON documents. It provides functionality to create, apply, and validate JSON Patch operations, making it easier to manage and manipulate JSON data structures in Java applications." +version: 1.0.0 +date-released: 2026-01-.. +doi: 10.5281/zenodo.??? +url: "https://github.com/kit-data-manager/JsonPatch" +repository-code: "https://github.com/kit-data-manager/JsonPatch" +license: Apache-2.0 diff --git a/codemeta.json b/codemeta.json new file mode 100644 index 0000000..d9a6e18 --- /dev/null +++ b/codemeta.json @@ -0,0 +1,91 @@ +{ + "@context": "https://w3id.org/codemeta/3.0", + "type": "SoftwareSourceCode", + "applicationCategory": "DeveloperApplication", + "applicationSubCategory": "JSON Processing", + "codeRepository": "https://github.com/kit-data-manager/JSONPatch.git", + "name": "JsonPatch", + "version": "1.0.0", + "dateCreated": "2025-12-17", + "datePublished": "2026-01-??", + "description": "JSONPatch is a Java library that implements the JSON Patch standard (RFC 6902) for applying partial modifications to JSON documents. It provides functionality to create, apply, and validate JSON Patch operations, making it easier to manage and manipulate JSON data structures in Java applications.", + "author": [ + { + "@type": "Person", + "givenName": "Volker", + "familyName": "Hartmann", + "email": "volker.hartmann@kit.edu", + "@id": "https://orcid.org/0000-0001-6383-5214", + "identifier": "https://orcid.org/0000-0001-6383-5214", + "affiliation": "Karlsruhe Institute of Technology (KIT)" + }, + { + "@type": "Person", + "givenName": "Thomas", + "familyName": "Jejkal", + "email": "thomas.jejkal@kit.edu", + "@id": "https://orcid.org/0000-0003-2804-688X", + "identifier": "https://orcid.org/0000-0003-2804-688X", + "affiliation": "Karlsruhe Institute of Technology (KIT)" + } + ], + "maintainer": [ + { + "@type": "Person", + "givenName": "Volker", + "familyName": "Hartmann", + "email": "volker.hartmann@kit.edu", + "@id": "https://orcid.org/0000-0001-6383-5214", + "identifier": "https://orcid.org/0000-0001-6383-5214", + "affiliation": "Karlsruhe Institute of Technology (KIT)" + }, + { + "@type": "Person", + "givenName": "Thomas", + "familyName": "Jejkal", + "email": "thomas.jejkal@kit.edu", + "@id": "https://orcid.org/0000-0003-2804-688X", + "identifier": "https://orcid.org/0000-0003-2804-688X", + "affiliation": "Karlsruhe Institute of Technology (KIT)" + } + ], + + "funder": [ + { + "@type": "Organization", + "name": "Helmholtz Association of German Research Centers", + "identifier": { + "@type": "PropertyValue", + "propertyID": "ROR", + "value": "https://ror.org/0281dp749" + }, + "sameAs": "https://www.helmholtz.de/" + }, + { + "@type": "Organization", + "name": "Helmholtz Metadata Collaboration (HMC)", + "identifier": { + "@type": "PropertyValue", + "propertyID": "ROR", + "value": "https://ror.org/04v4h0v24" + }, + "sameAs": "https://helmholtz-metadaten.de/" + } + ], + "keywords": [ + "Java", + "JSON", + "JSON Patch", + "RFC 6902", + "RFC 7396", + "Jackson 3", + "Library" + ], + "license": "https://spdx.org/licenses/Apache-2.0", + "runtimePlatform": "JVM", + "softwareRequirements": [ + "Java 17 or higher" + ], + "developmentStatus": "active", + "issueTracker": "https://github.com/kit-data-manager/JSONPatch/issues" +} From 81e26d71d03d36484deab26a229f855e1f653a0d Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Mon, 12 Jan 2026 09:23:20 +0100 Subject: [PATCH 04/16] Add a hook for updating CITATION.cff and codemeta.json while releasing a new version. --- build.gradle | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/build.gradle b/build.gradle index 892fd97..b4f7e60 100644 --- a/build.gradle +++ b/build.gradle @@ -7,6 +7,11 @@ plugins { id 'maven-publish' } +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import groovy.json.JsonSlurper +import groovy.json.JsonOutput + group = 'edu.kit.datamanager' @@ -39,6 +44,45 @@ dependencies { testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.10.2' } +def todayDate = LocalDate.now().format(DateTimeFormatter.ISO_DATE) + +// update CITATION.cff file (just date and version) +tasks.register('updateCitationFile') { + doLast { + def citationFile = file('CITATION.cff') + def lines = citationFile.readLines() + def map = ['version': project.version, 'date-released': todayDate] + map.each { key, value -> + lines = lines.collect { line -> + if (line.trim().startsWith("${key}:")) { + return "${key}: ${value}" + } else { + return line + } + } + } + citationFile.text = lines.join(System.lineSeparator()) + println "Updated CITATION.cff with version ${project.version} and date ${todayDate}" + } +} +// update codemeta.json file (just date and version) +tasks.register('updateCodemetaFile') { + doLast { + def codemetaFile = file('codemeta.json') + def codemeta = new JsonSlurper().parseText(codemetaFile.text) + codemeta.version = project.version + codemeta.softwareVersion = project.version + codemeta.dateModified = todayDate + + codemetaFile.text = JsonOutput.prettyPrint(JsonOutput.toJson(codemeta)) + println "Updated codemeta.json with (software)version ${project.version} and date ${todayDate}" + } +} +// Hook the update tasks into the release process +tasks.named('beforeReleaseBuild') { + dependsOn 'updateCitationFile', 'updateCodemetaFile' +} + test { useJUnitPlatform() } From 5bfcb3bc6fc17adb1e72971c1bbdae046618437d Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Mon, 12 Jan 2026 09:23:20 +0100 Subject: [PATCH 05/16] Removing metadata that is entered automatically by task 'beforeReleaseBuild'. --- CITATION.cff | 3 --- codemeta.json | 2 -- 2 files changed, 5 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index c4f184f..3567a44 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -14,9 +14,6 @@ authors: title: "JSONPatch" type: software abstract: "JSONPatch is a Java library that implements the JSON Patch standard (RFC 6902) for applying partial modifications to JSON documents. It provides functionality to create, apply, and validate JSON Patch operations, making it easier to manage and manipulate JSON data structures in Java applications." -version: 1.0.0 -date-released: 2026-01-.. -doi: 10.5281/zenodo.??? url: "https://github.com/kit-data-manager/JsonPatch" repository-code: "https://github.com/kit-data-manager/JsonPatch" license: Apache-2.0 diff --git a/codemeta.json b/codemeta.json index d9a6e18..17414d9 100644 --- a/codemeta.json +++ b/codemeta.json @@ -5,9 +5,7 @@ "applicationSubCategory": "JSON Processing", "codeRepository": "https://github.com/kit-data-manager/JSONPatch.git", "name": "JsonPatch", - "version": "1.0.0", "dateCreated": "2025-12-17", - "datePublished": "2026-01-??", "description": "JSONPatch is a Java library that implements the JSON Patch standard (RFC 6902) for applying partial modifications to JSON documents. It provides functionality to create, apply, and validate JSON Patch operations, making it easier to manage and manipulate JSON data structures in Java applications.", "author": [ { From 51a9dd9645c30a85cf03f91f348faf9437d9efcf Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Tue, 13 Jan 2026 10:35:03 +0100 Subject: [PATCH 06/16] Refactor code. --- README.md | 4 +- gradle.properties | 8 +-- .../kit/datamanager/util/json/JsonPatch.java | 26 ++++---- .../datamanager/util/json/JsonPatchUtil.java | 29 +++++---- .../util/json/JsonPatchAdditionalOpsTest.java | 20 +++--- .../util/json/JsonPatchIntegrationTest.java | 44 +++++++------ .../util/json/JsonPatchOperationTypeTest.java | 19 +++++- .../datamanager/util/json/JsonPatchTest.java | 34 +++++----- .../util/json/JsonPatchUtilErrorTest.java | 39 ++++++----- .../util/json/JsonPatchUtilTest.java | 64 +++++++++++-------- 10 files changed, 165 insertions(+), 122 deletions(-) diff --git a/README.md b/README.md index 32ed8f2..eb5632a 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,9 @@ import edu.kit.datamanager.util.json.JsonPatchUtil; public static class Person { public String name; public int age; } JsonMapper mapper = JsonMapper.builder().build(); -Person original = new Person(); original.name = "Alice"; original.age = 30; +Person original = new Person(); +original.name = "Alice"; +original.age = 30; String patchJson = "[ { \"op\": \"replace\", \"path\": \"/name\", \"value\": \"Bob\" } ]"; diff --git a/gradle.properties b/gradle.properties index a74a7d8..1ae0a94 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ -systemProp.jdk.tls.client.protocols="TLSv1,TLSv1.1,TLSv1.2" +systemProp.jdk.tls.client.protocols=TLSv1.2,TLSv1.3 -//----------------------------- -// Properties for build.gradle -//----------------------------- +# ----------------------------- +# Properties for build.gradle +# ----------------------------- version=1.0.0-SNAPSHOT diff --git a/src/main/java/edu/kit/datamanager/util/json/JsonPatch.java b/src/main/java/edu/kit/datamanager/util/json/JsonPatch.java index 1d1075a..cdfc9ac 100644 --- a/src/main/java/edu/kit/datamanager/util/json/JsonPatch.java +++ b/src/main/java/edu/kit/datamanager/util/json/JsonPatch.java @@ -21,9 +21,11 @@ import java.util.List; /** - * Json structure holding an array of patches. + * JSON structure holding an array of patches. + * + * @param operations List of operations in this patch. */ -public class JsonPatch { +public record JsonPatch(List operations) { /** * Enumeration of supported JSON Patch operations. */ @@ -38,13 +40,17 @@ public enum OperationType { /** * Use op for (JSON) serialization. + * * @return String representation of the operation */ @JsonValue - public String jsonValue() { return op; } + public String jsonValue() { + return op; + } /** * Ignore case for deserializing JSON value. + * * @param value JSON value * @return Corresponding OperationType */ @@ -66,26 +72,23 @@ public String toString() { } } - /** - * List of operations in this patch. - */ - private final List operations; /** * Constructor for JSON deserialization. * * @param operations List of operations in this patch. */ @JsonCreator(mode = JsonCreator.Mode.DELEGATING) - public JsonPatch(List operations) { - this.operations = operations; + public JsonPatch { } + /** * Get the list of operations in this patch. * * @return List of operations. */ + @Override @JsonValue - public List getOperations() { + public List operations() { return operations; } @@ -93,5 +96,6 @@ public List getOperations() { * Representation of a single JSON Patch operation as a nested record. * Accessible from outside as `JsonPatch.Operation`. */ - public record Operation(OperationType op, String path, String from, Object value) {} + public record Operation(OperationType op, String path, String from, Object value) { + } } \ No newline at end of file diff --git a/src/main/java/edu/kit/datamanager/util/json/JsonPatchUtil.java b/src/main/java/edu/kit/datamanager/util/json/JsonPatchUtil.java index 372110e..f7fda23 100644 --- a/src/main/java/edu/kit/datamanager/util/json/JsonPatchUtil.java +++ b/src/main/java/edu/kit/datamanager/util/json/JsonPatchUtil.java @@ -116,7 +116,7 @@ public static String applyPatch(String originalJson, String patchJson) throws Js * @throws JsonPatchProcessingException in case of processing errors */ public static String applyPatch(String originalJson, JsonPatch jsonPatch) throws JsonPatchProcessingException { - return applyPatch(originalJson, jsonPatch, (PatchOptions) null); + return applyPatch(originalJson, jsonPatch, null); } /** Applies a JSON Patch (RFC 6902) to a JSON string. * @param originalJson Original JSON as String @@ -125,7 +125,7 @@ public static String applyPatch(String originalJson, JsonPatch jsonPatch) throws * @throws JsonPatchProcessingException in case of processing errors */ public static String applyPatch(String originalJson, JsonNode patchJson) throws JsonPatchProcessingException { - return applyPatch(originalJson, patchJson, (PatchOptions) null); + return applyPatch(originalJson, patchJson, null); } /** Applies a JSON Patch (RFC 6902) to a JsonNode. * @@ -146,7 +146,7 @@ public static JsonNode applyPatch(JsonNode original, String jsonPatch) throws J * @throws JsonPatchProcessingException in case of processing errors */ public static JsonNode applyPatch(JsonNode original, JsonPatch jsonPatch) throws JsonPatchProcessingException { - return applyPatch(original, jsonPatch, (PatchOptions) null); + return applyPatch(original, jsonPatch, null); } /** * Applies a JSON Patch (RFC 6902) to a JsonNode. @@ -157,7 +157,7 @@ public static JsonNode applyPatch(JsonNode original, JsonPatch jsonPatch) throws * @throws JsonPatchProcessingException in case of processing errors */ public static JsonNode applyPatch(JsonNode original, JsonNode patch) throws JsonPatchProcessingException { - return applyPatch(original, patch, (PatchOptions) null); + return applyPatch(original, patch, null); } /** * Applies a JSON Patch (RFC 6902) to an object. @@ -174,8 +174,8 @@ public static T applyPatch(T original, String patchJson, Class type) thr /** * Applies a JSON Patch (RFC 6902) to an object represented by a string object. * (This could be used if you are still using Jackson 2.) - * @param originalJsonAsString Original objekt - * @param patchJson JSON Patch (Aaray of operations) + * @param originalJsonAsString Original object + * @param patchJson JSON Patch (Array of operations) * @param options optional Patch-Options (e.g. blocked paths), null values allowed * @return Patched JSON as String * @throws JsonPatchProcessingException in case of processing errors @@ -216,8 +216,8 @@ public static String applyPatch(String originalJsonAsString, String patchJson, P /** * Applies a JSON Patch (RFC 6902) to an object represented by a string object. * (This could be used if you are still using Jackson 2.) - * @param originalJsonAsString Original objekt - * @param patchJson JSON Patch (Aaray of operations) + * @param originalJsonAsString Original object + * @param patchJson JSON Patch (Array of operations) * @param options optional Patch-Options (e.g. blocked paths), null values allowed * @return Patched JSON as String * @throws JsonPatchProcessingException in case of processing errors @@ -228,8 +228,8 @@ public static String applyPatch(String originalJsonAsString, JsonPatch patchJson /** * Applies a JSON Patch (RFC 6902) to an object represented by a string object. * (This could be used if you are still using Jackson 2.) - * @param originalJsonAsString Original objekt - * @param patchJson JSON Patch (Aaray of operations) + * @param originalJsonAsString Original object + * @param patchJson JSON Patch (Array of operations) * @param options optional Patch-Options (e.g. blocked paths), null values allowed * @return Patched JSON as String * @throws JsonPatchProcessingException in case of processing errors @@ -277,8 +277,8 @@ public static JsonNode applyPatch(JsonNode original, JsonNode patch, PatchOption /** * Applies a JSON Patch (RFC 6902) to an object. * - * @param original Original objekt - * @param patchJson JSON Patch (Aaray of operations) + * @param original Original object + * @param patchJson JSON Patch (Array of operations) * @param type target type * @param options optional Patch-Options (e.g. blocked paths), null values allowed * @return Patched object @@ -405,7 +405,10 @@ public static T jsonStringToObject(String json, Class type) throws Json throw new JsonPatchProcessingException("Error while parsing JSON string to object!", e); } } - + /** Reset the JsonPatchUtil for testing purposes. + * This method clears any configured mapper settings and allows + * re-configuration. It should only be used in testing! + */ static void resetForTesting() { if (!Boolean.getBoolean("json.patch.resetForTesting")) { throw new IllegalStateException("Resetting JsonPatchUtil is only allowed in testing!"); diff --git a/src/test/java/edu/kit/datamanager/util/json/JsonPatchAdditionalOpsTest.java b/src/test/java/edu/kit/datamanager/util/json/JsonPatchAdditionalOpsTest.java index 7798d2b..281f6d7 100644 --- a/src/test/java/edu/kit/datamanager/util/json/JsonPatchAdditionalOpsTest.java +++ b/src/test/java/edu/kit/datamanager/util/json/JsonPatchAdditionalOpsTest.java @@ -27,7 +27,7 @@ public class JsonPatchAdditionalOpsTest { @Test - public void moveOperationMovesValue() throws Exception { + public void moveOperationMovesValue() { JsonMapper mapper = JsonMapper.builder().build(); JsonNode original = mapper.readTree("{\"a\":{\"value\":\"v\"},\"b\":{}}"); @@ -36,12 +36,12 @@ public void moveOperationMovesValue() throws Exception { JsonNode patched = JsonPatchUtil.applyPatch(original, patch); - assertEquals("v", patched.at("/b/value").asText()); + assertEquals("v", patched.at("/b/value").asString()); assertTrue(patched.at("/a/value").isMissingNode()); } @Test - public void copyOperationCopiesValue() throws Exception { + public void copyOperationCopiesValue() { JsonMapper mapper = JsonMapper.builder().build(); JsonNode original = mapper.readTree("{\"a\":{\"value\":\"v\"},\"b\":{}}"); @@ -50,12 +50,12 @@ public void copyOperationCopiesValue() throws Exception { JsonNode patched = JsonPatchUtil.applyPatch(original, patch); - assertEquals("v", patched.at("/b/value").asText()); - assertEquals("v", patched.at("/a/value").asText()); // original remains + assertEquals("v", patched.at("/b/value").asString()); + assertEquals("v", patched.at("/a/value").asString()); // original remains } @Test - public void testOperationSucceedsAndFails() throws Exception { + public void testOperationSucceedsAndFails() { JsonMapper mapper = JsonMapper.builder().build(); JsonNode original = mapper.readTree("{\"x\":1}"); @@ -72,7 +72,7 @@ public void testOperationSucceedsAndFails() throws Exception { } @Test - public void addToArrayWithDashAppends() throws Exception { + public void addToArrayWithDashAppends() { JsonMapper mapper = JsonMapper.builder().build(); JsonNode original = mapper.readTree("{\"arr\":[1,2]}\n"); @@ -85,7 +85,7 @@ public void addToArrayWithDashAppends() throws Exception { } @Test - public void removeArrayIndexRemovesElement() throws Exception { + public void removeArrayIndexRemovesElement() { JsonMapper mapper = JsonMapper.builder().build(); JsonNode original = mapper.readTree("{\"arr\":[1,2,3]}\n"); @@ -99,7 +99,7 @@ public void removeArrayIndexRemovesElement() throws Exception { } @Test - public void replaceNestedValue() throws Exception { + public void replaceNestedValue() { JsonMapper mapper = JsonMapper.builder().build(); JsonNode original = mapper.readTree("{\"obj\":{\"nested\":\"old\"}}\n"); @@ -107,7 +107,7 @@ public void replaceNestedValue() throws Exception { JsonPatch patch = new JsonPatch(ops); JsonNode patched = JsonPatchUtil.applyPatch(original, patch); - assertEquals("new", patched.at("/obj/nested").asText()); + assertEquals("new", patched.at("/obj/nested").asString()); } } diff --git a/src/test/java/edu/kit/datamanager/util/json/JsonPatchIntegrationTest.java b/src/test/java/edu/kit/datamanager/util/json/JsonPatchIntegrationTest.java index 0fa19dd..d2d010a 100644 --- a/src/test/java/edu/kit/datamanager/util/json/JsonPatchIntegrationTest.java +++ b/src/test/java/edu/kit/datamanager/util/json/JsonPatchIntegrationTest.java @@ -25,24 +25,25 @@ public class JsonPatchIntegrationTest { - // Static nested test POJOs so Jackson can construct/deserialise them + // Static nested test POJOs so Jackson can construct/deserialize them public static class InnerClass { public String name; public int[] numbers; } public static class Container { public InnerClass inner; } @Test - public void deepNestedReplaceAddMoveCopy() throws Exception { + public void deepNestedReplaceAddMoveCopy() { JsonMapper mapper = JsonMapper.builder().build(); - String originalJson = "{\n" + - " \"root\": {\n" + - " \"level1\": {\n" + - " \"level2\": {\n" + - " \"value\": \"old\",\n" + - " \"arr\": [ { \"id\": 1, \"items\": [\"a\",\"b\"] }, { \"id\": 2 } ]\n" + - " }\n" + - " },\n" + - " \"other\": { \"copyMe\": { \"f\": true } }\n" + - " }\n" + - "}"; + String originalJson = """ + { + "root": { + "level1": { + "level2": { + "value": "old", + "arr": [ { "id": 1, "items": ["a","b"] }, { "id": 2 } ] + } + }, + "other": { "copyMe": { "f": true } } + } + }"""; JsonNode original = mapper.readTree(originalJson); @@ -63,11 +64,11 @@ public void deepNestedReplaceAddMoveCopy() throws Exception { JsonNode patched = JsonPatchUtil.applyPatch(original, patch); // assertions - assertEquals("new", patched.at("/root/level1/level2/value").asText()); + assertEquals("new", patched.at("/root/level1/level2/value").asString()); // after adding at index 1, items should be ["a","x","b"] JsonNode items = patched.at("/root/level1/level2/arr/0/items"); assertEquals(3, items.size()); - assertEquals("x", items.get(1).asText()); + assertEquals("x", items.get(1).asString()); // moved id should exist assertEquals(1, patched.at("/root/other/movedId").asInt()); @@ -79,16 +80,17 @@ public void deepNestedReplaceAddMoveCopy() throws Exception { } @Test - public void applyPatchToPojoWithNestedObjects() throws Exception { + public void applyPatchToPojoWithNestedObjects() { Container original = new Container(); original.inner = new InnerClass(); original.inner.name = "foo"; original.inner.numbers = new int[]{1,2}; - String patchJson = "[" - + "{ \"op\": \"replace\", \"path\": \"/inner/name\", \"value\": \"bar\" }," - + "{ \"op\": \"add\", \"path\": \"/inner/numbers/-\", \"value\": 3 }" - + "]"; + String patchJson = """ + [ + { "op": "replace", "path": "/inner/name", "value": "bar" }, + { "op": "add", "path": "/inner/numbers/-", "value": 3 } + ]"""; Container patched = JsonPatchUtil.applyPatch(original, patchJson, Container.class); @@ -97,7 +99,7 @@ public void applyPatchToPojoWithNestedObjects() throws Exception { } @Test - public void complexSequenceWithArraysAndObjects() throws Exception { + public void complexSequenceWithArraysAndObjects() { JsonMapper mapper = JsonMapper.builder().build(); String originalJson = "{\"list\":[{\"k\":1},{\"k\":2},{\"k\":3}],\"map\":{\"a\":{\"v\":10}}}"; JsonNode original = mapper.readTree(originalJson); diff --git a/src/test/java/edu/kit/datamanager/util/json/JsonPatchOperationTypeTest.java b/src/test/java/edu/kit/datamanager/util/json/JsonPatchOperationTypeTest.java index f8ec130..1ffe382 100644 --- a/src/test/java/edu/kit/datamanager/util/json/JsonPatchOperationTypeTest.java +++ b/src/test/java/edu/kit/datamanager/util/json/JsonPatchOperationTypeTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2025 Karlsruhe Institute of Technology. + * + * 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 edu.kit.datamanager.util.json; import org.junit.jupiter.api.Test; @@ -34,9 +49,7 @@ public void testFromJson() { } @Test public void testFromJsonWithInvalidValue() { - Exception exception = assertThrows(IllegalArgumentException.class, () -> { - JsonPatch.OperationType.fromJson("invalid"); - }); + Exception exception = assertThrows(IllegalArgumentException.class, () -> JsonPatch.OperationType.fromJson("invalid")); assertEquals("Unknown operation type: invalid", exception.getMessage()); } @Test diff --git a/src/test/java/edu/kit/datamanager/util/json/JsonPatchTest.java b/src/test/java/edu/kit/datamanager/util/json/JsonPatchTest.java index ab4aef1..db7ba06 100644 --- a/src/test/java/edu/kit/datamanager/util/json/JsonPatchTest.java +++ b/src/test/java/edu/kit/datamanager/util/json/JsonPatchTest.java @@ -19,8 +19,8 @@ import tools.jackson.databind.json.JsonMapper; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import java.util.Map; import static org.junit.jupiter.api.Assertions.*; @@ -29,7 +29,7 @@ public class JsonPatchTest { @Test public void defaultConstructorShouldHaveNullOperations() { JsonPatch p = new JsonPatch(null); - assertNull(p.getOperations(), "operations should be null by default"); + assertNull(p.operations(), "operations should be null by default"); } @Test @@ -38,8 +38,8 @@ public void parameterizedConstructorAndGetters() { ops.add(new JsonPatch.Operation(JsonPatch.OperationType.ADD, "/a", null, "value")); JsonPatch p = new JsonPatch(ops); - assertSame(ops, p.getOperations()); - assertEquals(1, p.getOperations().size()); + assertSame(ops, p.operations()); + assertEquals(1, p.operations().size()); } @Test @@ -47,9 +47,9 @@ public void parameterizedConstructorAndGetters2() { JsonPatch.Operation op = new JsonPatch.Operation(JsonPatch.OperationType.ADD, "/a", null, "value"); List ops = new ArrayList<>(); ops.add(op); - JsonPatch p = new JsonPatch(Arrays.asList(op)); - assertSame(ops.getFirst(), p.getOperations().getFirst()); - assertEquals(1, p.getOperations().size()); + JsonPatch p = new JsonPatch(ops); + assertSame(ops.getFirst(), p.operations().getFirst()); + assertEquals(1, p.operations().size()); } @Test @@ -59,27 +59,27 @@ public void settersShouldUpdateOperationsAndAllowNulls() { ops.add(new JsonPatch.Operation(JsonPatch.OperationType.REMOVE, "/b", null, null)); p = new JsonPatch(ops); - assertSame(ops, p.getOperations()); + assertSame(ops, p.operations()); } @Test public void operationsListCanBeEmpty() { JsonPatch p = new JsonPatch(new ArrayList<>()); - assertNotNull(p.getOperations()); - assertTrue(p.getOperations().isEmpty()); + assertNotNull(p.operations()); + assertTrue(p.operations().isEmpty()); } @Test public void testSerializationAndDeserialization() { String json = """ [{"op":"replace","path":"/name","from":null,"value":"Bob"}, - {"op":"remove","path":"/address","from":null,"value":null}, + {"op":"remove","path":"/address","from":null,"value":null}, {"op":"add","path":"/age","from":null,"value":"30"}] """; JsonMapper mapper = JsonMapper.builder().build(); JsonPatch patch = mapper.readValue(json, JsonPatch.class); - assertNotNull(patch.getOperations()); - assertEquals(3, patch.getOperations().size()); + assertNotNull(patch.operations()); + assertEquals(3, patch.operations().size()); String jsonPatch = mapper.writeValueAsString(patch); assertNotNull(jsonPatch); assertEquals( json.replaceAll("\\s",""),jsonPatch); @@ -91,14 +91,14 @@ public void testAddingAnObject() { """; JsonMapper mapper = JsonMapper.builder().build(); JsonPatch patch = mapper.readValue(json, JsonPatch.class); - assertNotNull(patch.getOperations()); - assertEquals(1, patch.getOperations().size()); - JsonPatch.Operation op = patch.getOperations().get(0); + assertNotNull(patch.operations()); + assertEquals(1, patch.operations().size()); + JsonPatch.Operation op = patch.operations().get(0); assertEquals(JsonPatch.OperationType.ADD, op.op()); assertEquals("/person", op.path()); assertNull(op.from()); assertNotNull(op.value()); - assertTrue(op.value() instanceof java.util.Map); + assertInstanceOf(Map.class, op.value()); @SuppressWarnings("unchecked") java.util.Map valueMap = (java.util.Map)op.value(); assertEquals("Alice", valueMap.get("name")); diff --git a/src/test/java/edu/kit/datamanager/util/json/JsonPatchUtilErrorTest.java b/src/test/java/edu/kit/datamanager/util/json/JsonPatchUtilErrorTest.java index f729bdb..0c899ca 100644 --- a/src/test/java/edu/kit/datamanager/util/json/JsonPatchUtilErrorTest.java +++ b/src/test/java/edu/kit/datamanager/util/json/JsonPatchUtilErrorTest.java @@ -65,13 +65,14 @@ void testConstructorWithInitializeMapperAfterwards_throwsJsonProcessingException void testResetForTestingWhileNotAllowed_throwsIllegalStateException() { System.setProperty("json.patch.resetForTesting", "false"); JsonMapper defaultMapper = JsonPatchUtil.getDefaultMapper(); - assertThrows(IllegalStateException.class, () ->JsonPatchUtil.resetForTesting()); + assertNotNull(defaultMapper); + assertThrows(IllegalStateException.class, JsonPatchUtil::resetForTesting); System.setProperty("json.patch.resetForTesting", "true"); } @Test void testApplyJsonPatch_malformedJson_throwsPatchProcessingException() { - var original = new Customer("1", java.util.List.of("Milk"), "001-555-1234"); + var original = new Customer("1", java.util.List.of("Milk"), "001-111-1234"); String badPatch = "not-a-json"; assertThrows(JsonPatchProcessingException.class, @@ -80,7 +81,7 @@ void testApplyJsonPatch_malformedJson_throwsPatchProcessingException() { @Test void testApplyJsonPatch_patchNotArray_throwsPatchProcessingException() { - var original = new Customer("1", java.util.List.of("Milk"), "001-555-1234"); + var original = new Customer("2", java.util.List.of("Milk"), "001-222-1234"); // Object instead of array => RFC 6902 requires an array of operations String patchObject = "{\"op\":\"replace\",\"path\":\"/telephone\",\"value\":\"000\"}"; @@ -90,22 +91,22 @@ void testApplyJsonPatch_patchNotArray_throwsPatchProcessingException() { @Test void testApplyJsonPatch_addNonExistingField_throwsPatchProcessingException() { - var original = new Customer("1", java.util.List.of("Milk", "Eggs"), "001-555-1234"); + var original = new Customer("3", java.util.List.of("Milk", "Eggs"), "001-333-1234"); // Try to add a non-existing field - String patch = "[{ \"op\": \"add\", \"path\": \"/unkownField\", \"value\": \"SomeValue\" }]"; + String patch = "[{ \"op\": \"add\", \"path\": \"/unknownField\", \"value\": \"SomeValue\" }]"; // Should work with strings String patchedJson = JsonPatchUtil.applyPatch(JsonPatchUtil.jsonObjectToString(original), patch); - assertTrue(patchedJson.contains("\"unkownField\":\"SomeValue\"")); + assertTrue(patchedJson.contains("\"unknownField\":\"SomeValue\"")); assertThrows(JsonPatchProcessingException.class, () -> JsonPatchUtil.applyPatch(original, patch, Customer.class)); - String validPatch = "[{ \"op\": \"add\", \"path\": \"/telephone\", \"value\": \"001-555-6789\" }]"; + String validPatch = "[{ \"op\": \"add\", \"path\": \"/telephone\", \"value\": \"001-333-6789\" }]"; Customer patchedCustomer = JsonPatchUtil.applyPatch(original, validPatch, Customer.class); - assertEquals("001-555-6789", patchedCustomer.telephone()); + assertEquals("001-333-6789", patchedCustomer.telephone()); } @Test void testApplyJsonPatch_removeNonExistingPath_throwsPatchProcessingException() { - var original = new Customer("1", java.util.List.of("Milk", "Eggs"), "001-555-1234"); + var original = new Customer("4", java.util.List.of("Milk", "Eggs"), "001-444-1234"); // Try to remove non-existing index 10 String patch = "[{ \"op\": \"remove\", \"path\": \"/favorites/10\" }]"; @@ -115,7 +116,7 @@ void testApplyJsonPatch_removeNonExistingPath_throwsPatchProcessingException() { @Test void testApplyJsonPatch_fromBlockedPath_throwsPatchProcessingException() { - var original = new Customer("1", java.util.List.of("Milk"), "001-555-1234"); + var original = new Customer("5", java.util.List.of("Milk"), "001-555-1234"); // Try to move between paths, where "from" is blocked String patch = "[{ \"op\": \"move\", \"from\": \"/id\", \"path\": \"/telephone\" }]"; var options = PatchOptions.ofBlockedPaths(Set.of("/id")); @@ -126,7 +127,7 @@ void testApplyJsonPatch_fromBlockedPath_throwsPatchProcessingException() { @Test void testApplyJsonMergePatch_notObject_throwsPathProcessingException() { - var original = new Customer("1", java.util.List.of("Milk"), "001-555-1234"); + var original = new Customer("6", java.util.List.of("Milk"), "001-666-1234"); // Invalid merge patch String mergePatchArray = "[ \"telephone\", \"000\" ]"; @@ -136,7 +137,7 @@ void testApplyJsonMergePatch_notObject_throwsPathProcessingException() { @Test void testApplyPatch_addToNullArray_createsList() { - var original = new Customer("1", null, "001-555-1234"); + var original = new Customer("7", null, "001-777-1234"); String patch = "[{ \"op\": \"add\", \"path\": \"/favorites/-\", \"value\": \"Milk\" }]"; // Can't add to null array assertThrows(JsonPatchProcessingException.class, @@ -145,7 +146,7 @@ void testApplyPatch_addToNullArray_createsList() { @Test void testApplyPatch_addToNullArray_createsList1() { - var original = new Customer("1", null, "001-555-1234"); + var original = new Customer("8", null, "001-888-1234"); String patch = "[{ \"op\": \"add\", \"path\": \"/favorites/0\", \"value\": \"Milk\" }]"; // Can't add to null array assertThrows(JsonPatchProcessingException.class, @@ -154,7 +155,7 @@ void testApplyPatch_addToNullArray_createsList1() { @Test void testApplyMergePatch_setFieldToNull_resultsNull() { - var original = new Customer("1", java.util.List.of("Milk"), "001-555-1234"); + var original = new Customer("9", java.util.List.of("Milk"), "001-999-1234"); String mergePatch = "{\"telephone\":null}"; var result = JsonPatchUtil.applyMergePatch(original, mergePatch, Customer.class); @@ -166,18 +167,22 @@ void testApplyMergePatch_setFieldToNull_resultsNull() { void testApplyPatch_optionsWithEmptyBlockedPaths_allowsChange() { record CustomerMinimal(String id, String telephone) {} - var original = new CustomerMinimal("1", "001-555-1234"); + var original = new CustomerMinimal("1", "001-111-1234"); String patch = "[{\"op\":\"replace\",\"path\":\"/id\",\"value\":\"2\"}]"; - var options = PatchOptions.ofBlockedPaths(java.util.Set.of()); // leere Menge + var options = PatchOptions.ofBlockedPaths(java.util.Set.of()); // empty set var result = JsonPatchUtil.applyPatch(original, patch, CustomerMinimal.class, options); assertEquals("2", result.id()); + + var original2 = new CustomerMinimal("3", "001-222-1234"); + var result2 = JsonPatchUtil.applyPatch(original2, patch, CustomerMinimal.class, options); + assertEquals("2", result2.id()); } @Test void testGetJsonObjectToString_withInvalidObject_throwsJsonProcessingException() { - class EmptyClass {}; + class EmptyClass {} JsonPatchUtil.configureMapper(builder -> builder.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, true)); assertThrows(JsonPatchProcessingException.class, () -> JsonPatchUtil.jsonObjectToString(new EmptyClass())); diff --git a/src/test/java/edu/kit/datamanager/util/json/JsonPatchUtilTest.java b/src/test/java/edu/kit/datamanager/util/json/JsonPatchUtilTest.java index 9bd86c9..943d5ee 100644 --- a/src/test/java/edu/kit/datamanager/util/json/JsonPatchUtilTest.java +++ b/src/test/java/edu/kit/datamanager/util/json/JsonPatchUtilTest.java @@ -26,27 +26,47 @@ import static org.junit.jupiter.api.Assertions.*; public class JsonPatchUtilTest { + // simple POJO + record Person(String name, int age) {} @Test - public void applyPatchToJsonNode_happyPath() throws Exception { + public void applyPatchToJsonNodeWithJsonPatch() { JsonMapper mapper = JsonMapper.builder().build(); JsonNode original = mapper.readTree("{\"name\":\"Alice\",\"age\":30}"); List ops = List.of( - new JsonPatch.Operation(JsonPatch.OperationType.REPLACE, "/name", null, "Bob"), - new JsonPatch.Operation(JsonPatch.OperationType.ADD, "/city", null, "Karlsruhe") + new JsonPatch.Operation(JsonPatch.OperationType.REPLACE, "/name", null, "Bob"), + new JsonPatch.Operation(JsonPatch.OperationType.ADD, "/city", null, "Karlsruhe") ); JsonPatch patch = new JsonPatch(ops); JsonNode patched = JsonPatchUtil.applyPatch(original, patch); - assertEquals("Bob", patched.get("name").asText()); + assertEquals("Bob", patched.get("name").asString()); assertEquals(30, patched.get("age").asInt()); - assertEquals("Karlsruhe", patched.get("city").asText()); + assertEquals("Karlsruhe", patched.get("city").asString()); } @Test - public void applyPatchWithNonArrayPatchShouldThrow() throws Exception { + public void applyPatchToJsonNodeWithJsonNode() { + JsonMapper mapper = JsonMapper.builder().build(); + JsonNode original = mapper.readTree("{\"name\":\"Alice\",\"age\":30}"); + + List ops = List.of( + new JsonPatch.Operation(JsonPatch.OperationType.REPLACE, "/name", null, "Bob"), + new JsonPatch.Operation(JsonPatch.OperationType.ADD, "/city", null, "Karlsruhe") + ); + JsonNode patchNode = mapper.valueToTree(new JsonPatch(ops)); + + JsonNode patched = JsonPatchUtil.applyPatch(original, patchNode); + + assertEquals("Bob", patched.get("name").asString()); + assertEquals(30, patched.get("age").asInt()); + assertEquals("Karlsruhe", patched.get("city").asString()); + } + + @Test + public void applyPatchWithNonArrayPatchShouldThrow() { JsonMapper mapper = JsonMapper.builder().build(); JsonNode original = mapper.readTree("{\"name\":\"Alice\"}"); @@ -58,40 +78,34 @@ public void applyPatchWithNonArrayPatchShouldThrow() throws Exception { @Test public void applyPatchPojo_blockedPathShouldThrow() { - // simple POJO - class Person { public String name; public int age; } - - Person original = new Person(); - original.name = "Alice"; - original.age = 30; - + String name = "Betty"; + int age = 31; + Person original = new Person(name, age); String patchJson = "[ { \"op\": \"replace\", \"path\": \"/name\", \"value\": \"Bob\" } ]"; PatchOptions options = PatchOptions.ofBlockedPaths(Set.of("/name")); assertThrows(JsonPatchProcessingException.class, () -> JsonPatchUtil.applyPatch(original, patchJson, Person.class, options)); + assertEquals(name, original.name); + assertEquals(age, original.age); } @Test public void applyPatchPojo() { - // simple POJO - record Person (String name, int age) {} - - Person original = new Person("Alice", 30); + String name = "Caroline"; + int age = 32; + Person original = new Person(name, age); String patchJson = "[ { \"op\": \"replace\", \"path\": \"/name\", \"value\": \"Bob\" } ]"; Person updatedPerson = JsonPatchUtil.applyPatch(original, patchJson, Person.class); assertEquals("Bob", updatedPerson.name); - assertEquals(30, updatedPerson.age); + assertEquals(32, updatedPerson.age); } @Test - public void applyPatchPojo_withUnnownField() { - // simple POJO - record Person (String name, int age) {} - - Person original = new Person("Alice", 30); + public void applyPatchPojo_withUnknownField() { + Person original = new Person("Doreen", 33); String patchJson = "[ { \"op\": \"replace\", \"path\": \"/address\", \"value\": \"new address\" } ]"; @@ -144,7 +158,7 @@ public void applyPatch_jsonNode_string() { String patch = "[{ \"op\": \"replace\", \"path\": \"/b\", \"value\": 3 }]"; JsonNode jsonNode = JsonPatchUtil.applyPatch(JsonPatchUtil.jsonStringToNode(original), patch); - assertTrue(jsonNode.get("b").intValue() == 3); + assertEquals(3, jsonNode.get("b").intValue()); } @Test public void applyPatch_jsonNode_jsonPatch() { @@ -152,7 +166,7 @@ public void applyPatch_jsonNode_jsonPatch() { String patch = "[{ \"op\": \"replace\", \"path\": \"/b\", \"value\": 3 }]"; JsonNode jsonNode = JsonPatchUtil.applyPatch(JsonPatchUtil.jsonStringToNode(original), JsonPatchUtil.jsonStringToObject(patch, JsonPatch.class)); - assertTrue(jsonNode.get("b").intValue() == 3); + assertEquals(3, jsonNode.get("b").intValue()); } From 59a2248ad1cdbe146cbd637a0962d2eb7fb76d53 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Wed, 14 Jan 2026 11:34:16 +0100 Subject: [PATCH 07/16] Remove unnecessary package in build.gradle. --- build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/build.gradle b/build.gradle index b4f7e60..4740d66 100644 --- a/build.gradle +++ b/build.gradle @@ -33,8 +33,6 @@ dependencies { implementation 'org.slf4j:slf4j-api:2.0.9' api 'tools.jackson.core:jackson-databind:3.0.3' - api 'jakarta.json:jakarta.json-api:2.0.1' - api 'jakarta.json:jakarta.json-api:2.1.3' // API definition // implementation 'org.glassfish:jakarta.json:2.0.1' // reference implementation From 475de094f5a2bbed0ea75383fdbd8591825587ac Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Tue, 20 Jan 2026 10:27:00 +0100 Subject: [PATCH 08/16] Add CHANGELOG.md --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..94b3316 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,25 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Changed + +### Added + +### Fixed + +### Libs + +### Plugins + +### Github Actions + +## [0.9.0] - date 2026-01-20 +First release of JsonPatch library. + +[Unreleased]: https://github.com/kit-data-manager/metastore2/compare/v0.9.0...HEAD +[0.9.0]: https://github.com/kit-data-manager/metastore2/releases/tag/v0.9.0 \ No newline at end of file From d69b8872af5d0ef295b98e2e099f3045f33a8cb6 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Tue, 20 Jan 2026 10:27:33 +0100 Subject: [PATCH 09/16] Add template for bug reports. --- .github/ISSUE_TEMPLATE/bug_report.md | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..b2f6900 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] + +**Smartphone (please complete the following information):** +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. From 134fd30c16ab062ec01ae2313bb4f0e595b8426f Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Tue, 20 Jan 2026 10:30:24 +0100 Subject: [PATCH 10/16] Split build.gradle and add tasks for releasing and publishing versions. --- build.gradle | 108 +++++++++++++++++----------------- gradle/profile-deploy.gradle | 79 +++++++++++++++++++++++++ gradle/profile-release.gradle | 86 +++++++++++++++++++++++++++ 3 files changed, 219 insertions(+), 54 deletions(-) create mode 100644 gradle/profile-deploy.gradle create mode 100644 gradle/profile-release.gradle diff --git a/build.gradle b/build.gradle index 4740d66..6c63aac 100644 --- a/build.gradle +++ b/build.gradle @@ -7,15 +7,11 @@ plugins { id 'maven-publish' } -import java.time.LocalDate -import java.time.format.DateTimeFormatter -import groovy.json.JsonSlurper -import groovy.json.JsonOutput - +description = 'A small utility library for applying JSON Patches to JSON documents.' group = 'edu.kit.datamanager' -println "Building ${name} version: ${version}" +println "Building '${name}' version: '${version}'" println "Running gradle version: $gradle.gradleVersion" println "JDK version: ${JavaVersion.current()}" @@ -25,76 +21,80 @@ repositories { } java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} + +def buildPofile = project.findProperty("buildProfile") ?: "minimal" + +if (buildPofile == "deploy") { + println "Using release profile for building '${name}'" + apply from: '$rootDir/gradle/profile-deploy.gradle' +} + +//check if release build is requested +def requested = gradle.startParameter.taskNames +if (requested.any { it.toLowerCase().endsWith("release") }) { + apply from: "$rootDir/gradle/profile-release.gradle" } dependencies { + implementation 'org.eclipse.parsson:parsson:1.1.4' implementation 'org.slf4j:slf4j-api:2.0.9' - api 'tools.jackson.core:jackson-databind:3.0.3' api 'jakarta.json:jakarta.json-api:2.1.3' // API definition -// implementation 'org.glassfish:jakarta.json:2.0.1' // reference implementation - - implementation 'org.eclipse.parsson:parsson:1.1.4' + api 'tools.jackson.core:jackson-databind:3.0.3' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.10.2' } -def todayDate = LocalDate.now().format(DateTimeFormatter.ISO_DATE) - -// update CITATION.cff file (just date and version) -tasks.register('updateCitationFile') { - doLast { - def citationFile = file('CITATION.cff') - def lines = citationFile.readLines() - def map = ['version': project.version, 'date-released': todayDate] - map.each { key, value -> - lines = lines.collect { line -> - if (line.trim().startsWith("${key}:")) { - return "${key}: ${value}" - } else { - return line - } - } - } - citationFile.text = lines.join(System.lineSeparator()) - println "Updated CITATION.cff with version ${project.version} and date ${todayDate}" - } -} -// update codemeta.json file (just date and version) -tasks.register('updateCodemetaFile') { - doLast { - def codemetaFile = file('codemeta.json') - def codemeta = new JsonSlurper().parseText(codemetaFile.text) - codemeta.version = project.version - codemeta.softwareVersion = project.version - codemeta.dateModified = todayDate - - codemetaFile.text = JsonOutput.prettyPrint(JsonOutput.toJson(codemeta)) - println "Updated codemeta.json with (software)version ${project.version} and date ${todayDate}" - } -} -// Hook the update tasks into the release process -tasks.named('beforeReleaseBuild') { - dependsOn 'updateCitationFile', 'updateCodemetaFile' -} - test { useJUnitPlatform() } - +/////////////////////////////////////////////////////////////////////////////// +//for plugin maven-publish +//see https://docs.gradle.org/current/userguide/publishing_maven.html +/////////////////////////////////////////////////////////////////////////////// publishing { publications { mavenJava(MavenPublication) { from components.java - artifactId = 'JsonPatch' + artifactId = 'json-patch-util' + version = project.version + pom { + name = 'JSON Patch Utility Library' + description = 'A small utility library for applying JSON Patches to JSON documents.' + url = 'https://datamanager.kit.edu' + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'https://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id = 'Jejkal' + name = 'Thomas Jejkal' + email = 'webmaster@datamanager.kit.edu' + roles = ['developer', 'maintainer'] + } + developer { + id = 'HartmannV' + name = 'Volker Hartmann' + roles = ['developer', 'maintainer'] + } + } + scm { + connection = 'scm:git:github.com/kit-data-manager/JsonPatch' + developerConnection = 'scm:git:github.com/kit-data-manager/JsonPatch' + url = 'https://github.com/kit-data-manager/JsonPatch' + } + } } } repositories { mavenLocal() - } } diff --git a/gradle/profile-deploy.gradle b/gradle/profile-deploy.gradle new file mode 100644 index 0000000..04713cb --- /dev/null +++ b/gradle/profile-deploy.gradle @@ -0,0 +1,79 @@ +/* + * Copyright 2026 Karlsruhe Institute of Technology. + * + * 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. + */ +//////////////////////////////////////////////////////////////////////////////// +//for java plugin (package JavaDoc and Sources jars) +//see https://docs.gradle.org/current/userguide/java_plugin.html +//////////////////////////////////////////////////////////////////////////////// +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + withSourcesJar() + withJavadocJar() +} + +//////////////////////////////////////////////////////////////////////////////// +//for plugin io.github.gradle-nexus.publish-plugin +//see https://github.com/gradle-nexus/publish-plugin +//////////////////////////////////////////////////////////////////////////////// +nexusPublishing { + repositories { + // Migration to Sontatype Central + // see https://github.com/gradle-nexus/publish-plugin?tab=readme-ov-file#publishing-to-maven-central-via-sonatype-central + // see https://central.sonatype.org/publish/publish-portal-ossrh-staging-api/#configuration + sonatype { + nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) + snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) + } + // You need to set your Central credentials (those are different from the legacy OSSRH ones). + // To increase security, it is advised to use the user token's username and password pair + // (instead of regular username and password). + // Those values should be set as the + // sonatypeUsername and + // sonatypePassword project properties, e.g. in ~/.gradle/gradle.properties or + // via the + // ORG_GRADLE_PROJECT_sonatypeUsername and + // ORG_GRADLE_PROJECT_sonatypePassword environment variables. + // For generating the user token, see https://central.sonatype.org/publish/generate-portal-token/ + } +} + +//////////////////////////////////////////////////////////////////////////////// +//for plugin signing +//see https://docs.gradle.org/current/userguide/signing_plugin.html +//////////////////////////////////////////////////////////////////////////////// +signing { + //make signing required unless for SNAPSHOT releases or if signing is explicitly skipped + required { !project.version.endsWith("-SNAPSHOT") && !project.hasProperty("skipSigning") } + + //look for property 'signingKey' + if (project.findProperty("signingKey")) { + //If required, read a sub-key specified by its ID in property signingKeyId + //def signingKeyId = findProperty("signingKeyId") + //read property 'signingKey' + def signingKey = findProperty("signingKey") + //read property 'signingPassword' + def signingPassword = findProperty("signingPassword") + //Select to use in-memory ascii-armored keys + useInMemoryPgpKeys(signingKey, signingPassword) + //Only if also using signingKeyId + //useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + + //Apply signing to publication identity 'publishing.publications.maven' + sign publishing.publications.mavenJava + }else { + println 'WARNING: No property \'signingKey\' found. Artifact signing will be skipped.' + } +} \ No newline at end of file diff --git a/gradle/profile-release.gradle b/gradle/profile-release.gradle new file mode 100644 index 0000000..31805b7 --- /dev/null +++ b/gradle/profile-release.gradle @@ -0,0 +1,86 @@ +/* + * Copyright 2026 Karlsruhe Institute of Technology. + * + * 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. + */ +//////////////////////////////////////////////////////////////////////////////// +// Version tags start with 'v' e.g. v1.0.0 +// codemeta.json and CITATION.cff are updated before release +//////////////////////////////////////////////////////////////////////////////// +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import groovy.json.JsonSlurper +import groovy.json.JsonOutput + + +def todayDate = LocalDate.now().format(DateTimeFormatter.ISO_DATE) + +//////////////////////////////////////////////////////////////////////////////// +//for plugin net.researchgate.release +//see https://github.com/researchgate/gradle-release +//////////////////////////////////////////////////////////////////////////////// +release { + //define template for tagging, e.g. v1.0.0 + tagTemplate = 'v${version}' + //set source file of version property + versionPropertyFile = 'gradle.properties' + //set possible properties which may contain the version + versionProperties = ['version', 'mainversion'] + git { + //branch from where to release (default: main) + requireBranch.set('main') + } +} + +//////////////////////////////////////////////////////////////////////////////// +// update CITATION.cff file (just date and version) +//////////////////////////////////////////////////////////////////////////////// +tasks.register('updateCitationFile') { + doLast { + def citationFile = file('CITATION.cff') + def lines = citationFile.readLines() + def map = ['version': project.version, 'date-released': todayDate] + map.each { key, value -> + lines = lines.collect { line -> + if (line.trim().startsWith("${key}:")) { + return "${key}: ${value}" + } else { + return line + } + } + } + citationFile.text = lines.join(System.lineSeparator()) + println "Updated CITATION.cff with version ${project.version} and date ${todayDate}" + } +} +//////////////////////////////////////////////////////////////////////////////// +// update codemeta.json file (just date and version) +//////////////////////////////////////////////////////////////////////////////// +tasks.register('updateCodemetaFile') { + doLast { + def codemetaFile = file('codemeta.json') + def codemeta = new JsonSlurper().parseText(codemetaFile.text) + codemeta.version = project.version + codemeta.softwareVersion = project.version + codemeta.dateModified = todayDate + + codemetaFile.text = JsonOutput.prettyPrint(JsonOutput.toJson(codemeta)) + println "Updated codemeta.json with (software)version ${project.version} and date ${todayDate}" + } +} +//////////////////////////////////////////////////////////////////////////////// +// Hook the update tasks into the release process +//////////////////////////////////////////////////////////////////////////////// +tasks.named('beforeReleaseBuild') { + dependsOn 'updateCitationFile', 'updateCodemetaFile' +} From f8e67c4d884955b5e978f37103f72de64170c027 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Tue, 20 Jan 2026 10:32:04 +0100 Subject: [PATCH 11/16] Add configuration for renovate. --- renovate.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..2ae7365 --- /dev/null +++ b/renovate.json @@ -0,0 +1,4 @@ +{ + "labels": ["dependencies"], + "baseBranches": ["development"] +} From 52ff3bd1293a7b24561e8244df8854ddb706db01 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Tue, 20 Jan 2026 10:33:00 +0100 Subject: [PATCH 12/16] Rename project. --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 94ebf86..5d7de83 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -rootProject.name = 'JsonPatch' \ No newline at end of file +rootProject.name = 'json-patch-util' \ No newline at end of file From b76ddc7c81414d022873b3498a7709ba3dda0dd3 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Tue, 20 Jan 2026 10:38:08 +0100 Subject: [PATCH 13/16] Add several gitHub actions. --- .github/issue-branch.yml | 19 ++++ .github/workflows/branch-on-issue.yml | 16 +++ .github/workflows/codeql-analysis.yml | 123 ++++++++++++++++++++++ .github/workflows/gradle.yml | 64 +++++++++++ .github/workflows/publish-plugin-core.yml | 21 ++++ 5 files changed, 243 insertions(+) create mode 100644 .github/issue-branch.yml create mode 100644 .github/workflows/branch-on-issue.yml create mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 .github/workflows/gradle.yml create mode 100644 .github/workflows/publish-plugin-core.yml diff --git a/.github/issue-branch.yml b/.github/issue-branch.yml new file mode 100644 index 0000000..0ac686b --- /dev/null +++ b/.github/issue-branch.yml @@ -0,0 +1,19 @@ +# Automatically close issue after a pull request merge +autoCloseIssue: true + +# Override the source branch +defaultBranch: 'development' + +#Skip branch creation based on issue label 'question' +branches: + - label: question + skip: true + +# Automatically open a Pull Request +openPR: true + +# Copy attributes from issue +copyIssueDescriptionToPR: true +copyIssueLabelsToPR: true +copyIssueAssigneeToPR: true +copyIssueMilestoneToPR: true diff --git a/.github/workflows/branch-on-issue.yml b/.github/workflows/branch-on-issue.yml new file mode 100644 index 0000000..99a8f81 --- /dev/null +++ b/.github/workflows/branch-on-issue.yml @@ -0,0 +1,16 @@ +name: Branch on Issue + +on: + issues: + types: [ assigned ] + pull_request: + types: [ closed ] + +jobs: + create_issue_branch_job: + runs-on: ubuntu-latest + steps: + - name: Create Issue Branch + uses: robvanderleek/create-issue-branch@main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..7026252 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,123 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ main, development ] + pull_request: + # The branches below must be a subset of the branches above + branches: "*" + schedule: + - cron: '42 9 * * 4' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: write + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - uses: actions/checkout@v6 + - name: Set up Python3 + uses: actions/setup-python@v6 + with: + python-version: '3.14' + - name: Set up OpenJDK + uses: actions/setup-java@v5 + with: + distribution: 'zulu' + java-version: 17 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + # - name: Autobuild + # uses: github/codeql-action/autobuild@v2 + # with: + # java-version: 17 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + - run: | + echo "Run, Build Application using script" + ./gradlew build -DapplicationProperties="src/test/resources/test-config/application-test.properties" + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:java" + + review: + name: Review Dependencies and create SBOM + runs-on: ubuntu-latest + permissions: + actions: read + contents: write + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + steps: + - uses: actions/checkout@v6 + + - name: Set up Python3 + uses: actions/setup-python@v6 + with: + python-version: '3.14' + + - name: Set up OpenJDK + uses: actions/setup-java@v5 + with: + distribution: 'zulu' + java-version: 17 + + - name: 'Dependency Review' + uses: actions/dependency-review-action@v4 + with: + allow-licenses: MIT, Apache-2.0, ISC, BSD-2-Clause, 0BSD + base-ref: ${{ github.event.pull_request.base.sha || 'main' }} + head-ref: ${{ github.event.pull_request.head.sha || github.ref }} + + - name: Scan the image and upload dependency results + uses: anchore/sbom-action@v0 + with: + artifact-name: image.spdx.json + dependency-snapshot: true diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000..ba9ee9f --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,64 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: build with gradle + +on: + push: + branches: [ main ] + pull_request: + branches: [ main, development ] + +env: + # JDK version used for building jar file + currentBuildVersion: 21 +jobs: + build: + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: [ubuntu-latest, macOS-latest, windows-latest] + # Use both LTS releases and latest one for tests + jdk: [ 21, 25 ] + steps: + - name: Checkout repo + uses: actions/checkout@v6 + - name: Set up OpenJDK version ... + uses: actions/setup-java@v5 + with: + distribution: 'zulu' + java-version: ${{ matrix.jdk }} + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: | + if [ "$RUNNER_OS" == "Linux" ]; then + ./gradlew clean build + elif [ "$RUNNER_OS" == "macOS" ]; then + ./gradlew clean build + elif [ "$RUNNER_OS" == "Windows" ]; then + ./gradlew.bat clean build + else + echo "$RUNNER_OS not supported" + exit 1 + fi + shell: bash + coverage: + needs: build + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v6 + - name: Set up OpenJDK version ... + uses: actions/setup-java@v5 + with: + distribution: 'zulu' + java-version: ${{ env.currentBuildVersion }} + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle (JDK ${{ env.currentBuildVersion }}) + run: ./gradlew clean check jacocoTestReport + - name: Codecov + uses: codecov/codecov-action@v5 + with: + files: ./build/reports/jacoco/test/jacocoTestReport.xml #optional \ No newline at end of file diff --git a/.github/workflows/publish-plugin-core.yml b/.github/workflows/publish-plugin-core.yml new file mode 100644 index 0000000..e093f39 --- /dev/null +++ b/.github/workflows/publish-plugin-core.yml @@ -0,0 +1,21 @@ +name: Publish mapping-plugin-core to the Maven Central Repository +on: + release: + types: [published] +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Set up Java + uses: actions/setup-java@v5.1.0 + with: + java-version: 21 + distribution: 'zulu' # openjdk + - name: Publish package + run: ./gradlew -PbuildProfile=deploy publishToSonatype closeAndReleaseSonatypeStagingRepository + env: + ORG_GRADLE_PROJECT_sonatypeUsername : ${{ secrets.OSSRH_USERNAME }} + ORG_GRADLE_PROJECT_sonatypePassword : ${{ secrets.OSSRH_PASSWORD }} + ORG_GRADLE_PROJECT_signingKey : ${{ secrets.SIGNING_KEY }} + ORG_GRADLE_PROJECT_signingPassword : ${{ secrets.SIGNING_SECRET }} From d3e5e599e97b3bf1e48164cc59e96e6d3c88aa9e Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Tue, 20 Jan 2026 10:46:47 +0100 Subject: [PATCH 14/16] Add badges. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index eb5632a..63eb26d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +[![Build Status](https://github.com/kit-data-manager/JsonPatch/actions/workflows/gradle.yml/badge.svg)](https://github.com/kit-data-manager/JsonPatch/actions/workflows/gradle.yml) +[![Codecov](https://codecov.io/gh/kit-data-manager/JsonPatch/graph/badge.svg)](https://codecov.io/gh/kit-data-manager/JsonPatch) +[![CodeQL](https://github.com/kit-data-manager/JsonPatch/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/kit-data-manager/JsonPatch/actions/workflows/codeql-analysis.yml) +![License](https://img.shields.io/github/license/kit-data-manager/JsonPatch.svg) +![current Version](https://img.shields.io/github/v/release/kit-data-manager/JsonPatch) + # JSON Patch for Jackson 3 A small Java library that applies JSON Patch (RFC 6902) and JSON Merge Patch (RFC 7396) using Jakarta JSON-P and maps the results to POJOs with Jackson 3 (for example in Spring Boot 4). From afdcb8117053d7d082c51810480f0004098012e8 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Tue, 20 Jan 2026 11:10:22 +0100 Subject: [PATCH 15/16] Update Java version to 21 for code analysis. --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7026252..9375a16 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -16,7 +16,7 @@ on: branches: [ main, development ] pull_request: # The branches below must be a subset of the branches above - branches: "*" + branches: [ main, development ] schedule: - cron: '42 9 * * 4' @@ -46,7 +46,7 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'zulu' - java-version: 17 + java-version: 21 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL @@ -107,7 +107,7 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'zulu' - java-version: 17 + java-version: 21 - name: 'Dependency Review' uses: actions/dependency-review-action@v4 From 1a9036b25212e660ed8b230ca17251f5122ba14f Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Tue, 20 Jan 2026 11:32:17 +0100 Subject: [PATCH 16/16] Add code coverage with JaCoCo. --- build.gradle | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6c63aac..06e0e94 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,7 @@ plugins { id 'net.researchgate.release' version '3.1.0' id 'io.github.gradle-nexus.publish-plugin' version '2.0.0' id 'maven-publish' + id 'jacoco' } description = 'A small utility library for applying JSON Patches to JSON documents.' @@ -48,11 +49,27 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.10.2' } - +///////////////////////////////////////////////////////////////////////////////// +// Code coverage configuration with JaCoCo +///////////////////////////////////////////////////////////////////////////////// test { + finalizedBy jacocoTestReport useJUnitPlatform() } +jacoco { + toolVersion = "0.8.14" +} + +jacocoTestReport { + dependsOn test + reports { + xml.required = true + csv.required = false + html.outputLocation = layout.buildDirectory.dir("jacocoHtml") + } +} + /////////////////////////////////////////////////////////////////////////////// //for plugin maven-publish //see https://docs.gradle.org/current/userguide/publishing_maven.html