Repository: victools/jsonschema-generator (java)

Issue:
Duplicate entries in `required`
I'm creating a schema for my entity with the `JakartaValidationModule` and `Swagger2Module`. I have an interface that has a method annotated with `@Schema(requiredMode = REQUIRED)`. The interface has a sub-interface and there's a class implementing the sub-interface. Neither have additional validation or schema annotations.  
In the schema, the property is contained in the `required` array twice.  
I'm still trying to find out why, please let me know if you need additional information.

Pull request description:
fix: Avoid duplicate items in allOf and required arrays
When performing `allOf` clean-up, avoid duplicate entries in `allOf` and `required` arrays.

Fixes #499.

Patch:
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaCleanUpUtils.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaCleanUpUtils.java
index 4d95ba8..4b81153 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaCleanUpUtils.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaCleanUpUtils.java
@@ -271,8 +271,11 @@ public class SchemaCleanUpUtils {
             return null;
         }
         return () -> {
+            Set<JsonNode> arrayItems = new LinkedHashSet<>();
+            arrayNodesToMerge.forEach(node -> node.forEach(arrayItems::add));
+
             ArrayNode mergedArrayNode = this.config.createArrayNode();
-            arrayNodesToMerge.forEach(node -> node.forEach(mergedArrayNode::add));
+            arrayItems.forEach(mergedArrayNode::add);
             return mergedArrayNode;
         };
     }


Test patch:
diff --git a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaCleanUpUtilsTest.java b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaCleanUpUtilsTest.java
index 5d7797d..f8d916d 100644
--- a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaCleanUpUtilsTest.java
+++ b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaCleanUpUtilsTest.java
@@ -16,25 +16,17 @@
 
 package com.github.victools.jsonschema.generator.impl;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.github.victools.jsonschema.generator.OptionPreset;
-import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
-import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
 import com.github.victools.jsonschema.generator.SchemaVersion;
 import java.util.Collections;
 import java.util.stream.Stream;
-import org.json.JSONArray;
-import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.TestInstance;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.ArgumentsSource;
 import org.junit.jupiter.params.provider.MethodSource;
-import org.junit.jupiter.params.provider.ValueSource;
-import org.mockito.Mock;
 import org.skyscreamer.jsonassert.JSONAssert;
 import org.skyscreamer.jsonassert.JSONCompareMode;
 
@@ -116,4 +108,34 @@ public class SchemaCleanUpUtilsTest {
         String schemaAsString = schema.toString();
         JSONAssert.assertEquals('\n' + schemaAsString +'\n', expectedOutput, schemaAsString, JSONCompareMode.STRICT);
     }
+
+    Stream<Arguments> parametersForTestReduceAllOfNodes() {
+        return Stream.of(
+                Arguments.of(
+                        "{\"allOf\":[{\"type\":\"object\",\"properties\":{\"a\":{\"type\":\"string\"}}}," +
+                                "{\"type\":\"object\",\"properties\":{\"a\":{\"type\":\"string\"},\"b\":{\"type\":\"string\"}}}]}",
+                        "{\"type\":\"object\",\"properties\":{\"a\":{\"type\":\"string\"},\"b\":{\"type\":\"string\"}}}"),
+                Arguments.of(
+                        "{\"allOf\":[{\"type\":\"object\",\"properties\":{\"a\":{\"type\":\"string\"}},\"required\":[\"a\"]}," +
+                                "{\"type\":\"object\",\"properties\":{\"a\":{\"type\":\"string\"}},\"required\":[\"a\"]}]}",
+                        "{\"type\":\"object\",\"properties\":{\"a\":{\"type\":\"string\"}},\"required\":[\"a\"]}"),
+                Arguments.of(
+                        "{\"allOf\":[{\"type\":\"object\",\"properties\":{\"a\":{\"type\":\"string\"}},\"required\":[\"a\"]}," +
+                                "{\"type\":\"object\",\"properties\":{\"b\":{\"type\":\"string\"}},\"required\":[\"a\",\"b\"]}]}",
+                        "{\"type\":\"object\",\"properties\":{\"a\":{\"type\":\"string\"},\"b\":{\"type\":\"string\"}},\"required\":[\"a\",\"b\"]}")
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("parametersForTestReduceAllOfNodes")
+    public void testReduceAllOfNodes(String schemaInput, String expectedOutput) throws Exception {
+        SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON);
+        SchemaCleanUpUtils utilsInstance = new SchemaCleanUpUtils(configBuilder.build());
+
+        JsonNode schema = configBuilder.getObjectMapper().readTree(schemaInput);
+        utilsInstance.reduceAllOfNodes(Collections.singletonList((ObjectNode) schema));
+
+        String schemaAsString = schema.toString();
+        JSONAssert.assertEquals('\n' + schemaAsString + '\n', expectedOutput, schemaAsString, JSONCompareMode.STRICT);
+    }
 }