Repository: victools/jsonschema-generator
Pull request #441 (2024-03-29 14:51:10)

feat: extend support for void methods
Change in response to #440.

When a custom property definition is provided for a `void` method, it is now being considered and applied.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3601b9a..7743e5a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ 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]
+### `jsonschema-generator`
+#### Added
+- check for custom definitions for `void` methods (this may result in exceptions inside custom configuration if a `null` return type is not considered)
+
+#### Changed
+- if present, apply custom definition for `void` methods
 
 ## [4.34.0] - 2024-03-14
 ### `jsonschema-generator`
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java
index 4c4681c..a497745 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGenerationContextImpl.java
@@ -592,7 +592,12 @@ public class SchemaGenerationContextImpl implements SchemaGenerationContext {
      */
     private JsonNode createMethodSchema(MemberDetails<MethodScope> methodDetails) {
         if (methodDetails.getScope().isVoid()) {
-            return BooleanNode.FALSE;
+            // since 4.35.0: support custom definitions for void methods
+            CustomDefinition customDefinition = this.generatorConfig.getCustomDefinition(methodDetails.getScope(), this,
+                    methodDetails.getIgnoredDefinitionProvider());
+            if (customDefinition == null) {
+                return BooleanNode.FALSE;
+            }
         }
         ObjectNode subSchema = this.generatorConfig.createObjectNode();
         ObjectNode methodAttributes = AttributeCollector.collectMethodAttributes(methodDetails.getScope(), this);
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/FlattenedWrapperModule.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/FlattenedWrapperModule.java
index 7db0a59..716f46a 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/FlattenedWrapperModule.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/FlattenedWrapperModule.java
@@ -50,7 +50,7 @@ public class FlattenedWrapperModule<W> implements Module {
      * @return whether the given type is deemed to be of the targeted wrapper type in the context of this module
      */
     protected boolean isWrapperType(ResolvedType type) {
-        return this.wrapperType.isAssignableFrom(type.getErasedType());
+        return type != null && this.wrapperType.isAssignableFrom(type.getErasedType());
     }
 
     /**
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java
index 4364f13..6d0f658 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/module/SimpleTypeModule.java
@@ -283,13 +283,14 @@ public class SimpleTypeModule implements Module {
     }
 
     private boolean shouldHaveEmptySchema(TypeScope scope) {
-        return SchemaKeyword.TAG_TYPE_NULL == this.fixedJsonSchemaTypes.get(scope.getType().getErasedType());
+        return scope.getType() == null
+               || SchemaKeyword.TAG_TYPE_NULL == this.fixedJsonSchemaTypes.get(scope.getType().getErasedType());
     }
 
     /**
      * Implementation of the {@link CustomDefinitionProviderV2} interface for applying fixed schema definitions for simple java types.
      */
-    private class SimpleTypeDefinitionProvider implements CustomDefinitionProviderV2 {
+    private final class SimpleTypeDefinitionProvider implements CustomDefinitionProviderV2 {
 
         @Override
         public CustomDefinition provideCustomSchemaDefinition(ResolvedType javaType, SchemaGenerationContext context) {


Test patch:
diff --git a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorCustomDefinitionsTest.java b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorCustomDefinitionsTest.java
index 39f814a..21a1c66 100644
--- a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorCustomDefinitionsTest.java
+++ b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorCustomDefinitionsTest.java
@@ -26,8 +26,11 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.EnumSource;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.skyscreamer.jsonassert.JSONCompareMode;
 
 /**
  * Test for {@link SchemaGenerator} class.
@@ -229,6 +232,25 @@ public class SchemaGeneratorCustomDefinitionsTest {
         TestUtils.assertGeneratedSchema(result, this.getClass(), "custom-property-definition-" + schemaVersion.name() + ".json");
     }
 
+    @Test
+    public void testGenerateSchema_CustomPropertyDefinitionForVoidMethod() throws Exception {
+        CustomPropertyDefinitionProvider<MethodScope> customPropertyDefinitionProvider = (method, context) -> {
+            if (method.isVoid()) {
+                return new CustomPropertyDefinition(context.getGeneratorConfig().createObjectNode()
+                        .put(context.getKeyword(SchemaKeyword.TAG_DESCRIPTION), "this method is void"));
+            }
+            return null;
+        };
+        SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2020_12, OptionPreset.JAVA_OBJECT);
+        configBuilder.with(Option.VOID_METHODS);
+        configBuilder.forMethods()
+                .withCustomDefinitionProvider(customPropertyDefinitionProvider);
+        SchemaGenerator generator = new SchemaGenerator(configBuilder.build());
+        JsonNode result = generator.generateSchema(TestClassWithVoidMethod.class);
+        JSONAssert.assertEquals("{\"type\":\"object\",\"properties\":{\"updateSomething()\":{\"description\":\"this method is void\"}}}",
+                result.toString(), JSONCompareMode.STRICT);
+    }
+
     private static class TestDirectCircularClass {
 
         public int number;
@@ -256,4 +278,10 @@ public class SchemaGeneratorCustomDefinitionsTest {
         public List<TestCircularClass1> list1;
 
     }
+
+    private static class TestClassWithVoidMethod {
+        public void updateSomething() {
+            // perform an action
+        }
+    }
 }