From f1182b440116bb5551c3de48a44f0cf657779ab0 Mon Sep 17 00:00:00 2001 From: Yona Appletree Date: Mon, 18 Sep 2017 11:46:17 -0700 Subject: [PATCH] Fix for #174 - Support for using Jackson2's concept of required to control emitted property optionality --- .../typescript/generator/Settings.java | 1 + .../generator/parser/Jackson2Parser.java | 6 +++ .../generator/Jackson2ParserTest.java | 40 ++++++++++++++++++- .../generator/gradle/GenerateTask.java | 28 ++++++++++--- .../generator/maven/GenerateMojo.java | 8 ++++ 5 files changed, 75 insertions(+), 8 deletions(-) diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java index 720d9be4a..804432b23 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java @@ -70,6 +70,7 @@ public class Settings { public String typescriptVersion = "^2.4"; public boolean displaySerializerWarning = true; public boolean disableJackson2ModuleDiscovery = false; + public boolean useJackson2RequiredForOptional = false; public ClassLoader classLoader = null; private boolean defaultStringEnumsOverriddenByExtension = false; diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/parser/Jackson2Parser.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/parser/Jackson2Parser.java index e316ea5c5..311a83485 100644 --- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/parser/Jackson2Parser.java +++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/parser/Jackson2Parser.java @@ -80,7 +80,13 @@ private BeanModel parseBean(SourceType> sourceClass) { continue; } } + boolean optional = false; + + if (settings.useJackson2RequiredForOptional) { + optional = ! beanPropertyWriter.isRequired(); + } + for (Class optionalAnnotation : settings.optionalAnnotations) { if (beanPropertyWriter.getAnnotation(optionalAnnotation) != null) { optional = true; diff --git a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/Jackson2ParserTest.java b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/Jackson2ParserTest.java index 00fe338a7..def26db70 100644 --- a/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/Jackson2ParserTest.java +++ b/typescript-generator-core/src/test/java/cz/habarta/typescript/generator/Jackson2ParserTest.java @@ -1,11 +1,18 @@ package cz.habarta.typescript.generator; -import cz.habarta.typescript.generator.parser.*; -import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import cz.habarta.typescript.generator.parser.BeanModel; +import cz.habarta.typescript.generator.parser.Jackson2Parser; +import cz.habarta.typescript.generator.parser.Model; import org.junit.Assert; import org.junit.Test; +import javax.xml.bind.annotation.XmlElement; + public class Jackson2ParserTest { @@ -60,6 +67,24 @@ public void testTaggedUnion() { Assert.assertEquals("Jackson2ParserTest$SubTypeDiscriminatedByName3", bean3.getDiscriminantLiteral()); } + @Test + public void testNonJacksonRequiredOptional() { + final Settings settings = new Settings(); + settings.useJackson2RequiredForOptional = true; + + final Jackson2Parser jacksonParser = new Jackson2Parser(settings, new DefaultTypeProcessor()); + + final Model model = jacksonParser.parseModel(NonJacksonRequiredOptionalBean.class); + + final BeanModel bean = model.getBean(NonJacksonRequiredOptionalBean.class); + + Assert.assertEquals("required", bean.getProperties().get(0).getName()); + Assert.assertFalse(bean.getProperties().get(0).isOptional()); + + Assert.assertEquals("optional", bean.getProperties().get(1).getName()); + Assert.assertTrue(bean.getProperties().get(1).isOptional()); + } + static Jackson2Parser getJackson2Parser() { final Settings settings = new Settings(); return new Jackson2Parser(settings, new DefaultTypeProcessor()); @@ -94,4 +119,15 @@ private static class SubTypeDiscriminatedByName2 implements ParentWithNameDiscri private static class SubTypeDiscriminatedByName3 implements ParentWithNameDiscriminant { } + private static class NonJacksonRequiredOptionalBean { + // Note: We use @XmlElement instead of @JsonProperty because the Jaxb annotations processing logic takes + // precedence even when there are no jaxb annotations on the members, due to what seems like a bug in + // com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector.hasRequiredMarker() + + @XmlElement(required = true) + public String required; + + @XmlElement(required = false) + public String optional; + } } diff --git a/typescript-generator-gradle-plugin/src/main/java/cz/habarta/typescript/generator/gradle/GenerateTask.java b/typescript-generator-gradle-plugin/src/main/java/cz/habarta/typescript/generator/gradle/GenerateTask.java index d22227373..cafe764a9 100644 --- a/typescript-generator-gradle-plugin/src/main/java/cz/habarta/typescript/generator/gradle/GenerateTask.java +++ b/typescript-generator-gradle-plugin/src/main/java/cz/habarta/typescript/generator/gradle/GenerateTask.java @@ -1,13 +1,27 @@ package cz.habarta.typescript.generator.gradle; -import cz.habarta.typescript.generator.*; +import cz.habarta.typescript.generator.ClassMapping; +import cz.habarta.typescript.generator.DateMapping; +import cz.habarta.typescript.generator.EnumMapping; import cz.habarta.typescript.generator.Input; -import java.io.*; -import java.net.*; -import java.util.*; -import org.gradle.api.*; -import org.gradle.api.tasks.*; +import cz.habarta.typescript.generator.JaxrsNamespacing; +import cz.habarta.typescript.generator.JsonLibrary; +import cz.habarta.typescript.generator.Output; +import cz.habarta.typescript.generator.Settings; +import cz.habarta.typescript.generator.StringQuotes; +import cz.habarta.typescript.generator.TypeScriptFileType; +import cz.habarta.typescript.generator.TypeScriptGenerator; +import cz.habarta.typescript.generator.TypeScriptOutputKind; +import org.gradle.api.DefaultTask; +import org.gradle.api.Task; +import org.gradle.api.tasks.TaskAction; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; public class GenerateTask extends DefaultTask { @@ -63,6 +77,7 @@ public class GenerateTask extends DefaultTask { public StringQuotes stringQuotes; public boolean displaySerializerWarning = true; public boolean disableJackson2ModuleDiscovery; + public boolean useJackson2RequiredForOptional; public boolean debug; @TaskAction @@ -137,6 +152,7 @@ public void generate() throws Exception { settings.setStringQuotes(stringQuotes); settings.displaySerializerWarning = displaySerializerWarning; settings.disableJackson2ModuleDiscovery = disableJackson2ModuleDiscovery; + settings.useJackson2RequiredForOptional = useJackson2RequiredForOptional; settings.classLoader = classLoader; final File output = outputFile != null ? getProject().file(outputFile) diff --git a/typescript-generator-maven-plugin/src/main/java/cz/habarta/typescript/generator/maven/GenerateMojo.java b/typescript-generator-maven-plugin/src/main/java/cz/habarta/typescript/generator/maven/GenerateMojo.java index 0d5c50b7d..805b90a5a 100644 --- a/typescript-generator-maven-plugin/src/main/java/cz/habarta/typescript/generator/maven/GenerateMojo.java +++ b/typescript-generator-maven-plugin/src/main/java/cz/habarta/typescript/generator/maven/GenerateMojo.java @@ -409,6 +409,13 @@ public class GenerateMojo extends AbstractMojo { @Parameter private boolean disableJackson2ModuleDiscovery; + /** + * Use Jackson's concept of required parameters to specify optionality in the generated definition. If a property + * is not marked as required, the TypeScript property will be emitted as optional. + */ + @Parameter + private boolean useJackson2RequiredForOptional; + /** * Turns on verbose output for debugging purposes. */ @@ -483,6 +490,7 @@ public void execute() { settings.setStringQuotes(stringQuotes); settings.displaySerializerWarning = displaySerializerWarning; settings.disableJackson2ModuleDiscovery = disableJackson2ModuleDiscovery; + settings.useJackson2RequiredForOptional = useJackson2RequiredForOptional; settings.classLoader = classLoader; final File output = outputFile != null ? outputFile