Skip to content

Commit 9f6da7a

Browse files
requiredAnnotations parameter (#443)
- opposite of `optionalAnnotations` - properties optional by default - properties required when annotated with specified annotation
1 parent 89e695f commit 9f6da7a

File tree

5 files changed

+74
-3
lines changed

5 files changed

+74
-3
lines changed

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ public class Settings {
110110
public List<Class<? extends Annotation>> includePropertyAnnotations = new ArrayList<>();
111111
public List<Class<? extends Annotation>> excludePropertyAnnotations = new ArrayList<>();
112112
public List<Class<? extends Annotation>> optionalAnnotations = new ArrayList<>();
113+
public List<Class<? extends Annotation>> requiredAnnotations = new ArrayList<>();
113114
public List<Class<? extends Annotation>> nullableAnnotations = new ArrayList<>();
114115
public boolean generateInfoJson = false;
115116
public boolean generateNpmPackageJson = false;
@@ -237,6 +238,10 @@ public void loadOptionalAnnotations(ClassLoader classLoader, List<String> option
237238
this.optionalAnnotations = loadClasses(classLoader, optionalAnnotations, Annotation.class);
238239
}
239240

241+
public void loadRequiredAnnotations(ClassLoader classLoader, List<String> requiredAnnotations) {
242+
this.requiredAnnotations = loadClasses(classLoader, requiredAnnotations, Annotation.class);
243+
}
244+
240245
public void loadNullableAnnotations(ClassLoader classLoader, List<String> nullableAnnotations) {
241246
this.nullableAnnotations = loadClasses(classLoader, nullableAnnotations, Annotation.class);
242247
}
@@ -345,6 +350,9 @@ public void validate() {
345350
annotation.getName()));
346351
}
347352
}
353+
if (!optionalAnnotations.isEmpty() && !requiredAnnotations.isEmpty()) {
354+
throw new RuntimeException("Only one of 'optionalAnnotations' and 'requiredAnnotations' can be used at the same time.");
355+
}
348356
for (Class<? extends Annotation> annotation : nullableAnnotations) {
349357
final Target target = annotation.getAnnotation(Target.class);
350358
final List<ElementType> elementTypes = target != null ? Arrays.asList(target.value()) : Arrays.asList();

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/parser/ModelParser.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,12 @@ protected boolean isPropertyOptional(PropertyMember propertyMember) {
159159
return true;
160160
}
161161
if (settings.optionalProperties == null || settings.optionalProperties == OptionalProperties.useSpecifiedAnnotations) {
162-
return Utils.hasAnyAnnotation(propertyMember::getAnnotation, settings.optionalAnnotations);
162+
if (!settings.optionalAnnotations.isEmpty()) {
163+
return Utils.hasAnyAnnotation(propertyMember::getAnnotation, settings.optionalAnnotations);
164+
}
165+
if (!settings.requiredAnnotations.isEmpty()) {
166+
return !Utils.hasAnyAnnotation(propertyMember::getAnnotation, settings.requiredAnnotations);
167+
}
163168
}
164169
return false;
165170
}

typescript-generator-core/src/test/java/cz/habarta/typescript/generator/OptionalAnnotationTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.lang.annotation.Retention;
1111
import java.lang.annotation.RetentionPolicy;
1212
import java.lang.annotation.Target;
13+
import java.util.Arrays;
1314
import org.junit.Assert;
1415
import org.junit.Test;
1516

@@ -161,4 +162,50 @@ public Long getSelectedId() {
161162
public @interface TypescriptOptional {
162163
}
163164

165+
@Test
166+
public void testOptionalAndRequiredProperty() {
167+
{
168+
final Settings settings = TestUtils.settings();
169+
settings.optionalAnnotations = Arrays.asList();
170+
settings.requiredAnnotations = Arrays.asList();
171+
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(ClassWithMarkedField.class));
172+
Assert.assertTrue(output.contains("a: string;"));
173+
Assert.assertTrue(output.contains("b: string;"));
174+
}
175+
{
176+
final Settings settings = TestUtils.settings();
177+
settings.optionalAnnotations = Arrays.asList(MarkerAnnotation.class);
178+
settings.requiredAnnotations = Arrays.asList();
179+
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(ClassWithMarkedField.class));
180+
Assert.assertTrue(output.contains("a: string;"));
181+
Assert.assertTrue(output.contains("b?: string;"));
182+
}
183+
{
184+
final Settings settings = TestUtils.settings();
185+
settings.optionalAnnotations = Arrays.asList();
186+
settings.requiredAnnotations = Arrays.asList(MarkerAnnotation.class);
187+
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(ClassWithMarkedField.class));
188+
Assert.assertTrue(output.contains("a?: string;"));
189+
Assert.assertTrue(output.contains("b: string;"));
190+
}
191+
try {
192+
final Settings settings = TestUtils.settings();
193+
settings.optionalAnnotations = Arrays.asList(MarkerAnnotation.class);
194+
settings.requiredAnnotations = Arrays.asList(MarkerAnnotation.class);
195+
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(ClassWithMarkedField.class));
196+
Assert.fail();
197+
} catch (Exception e) {
198+
// expected - optionalAnnotations and requiredAnnotations cannot be used together
199+
}
200+
}
201+
202+
public class ClassWithMarkedField {
203+
public String a;
204+
@MarkerAnnotation public String b;
205+
}
206+
207+
@Retention(RetentionPolicy.RUNTIME)
208+
public @interface MarkerAnnotation {
209+
}
210+
164211
}

typescript-generator-gradle-plugin/src/main/java/cz/habarta/typescript/generator/gradle/GenerateTask.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ public class GenerateTask extends DefaultTask {
103103
public List<String> extensions;
104104
public List<Settings.ConfiguredExtension> extensionsWithConfiguration;
105105
public List<String> optionalAnnotations;
106+
public List<String> requiredAnnotations;
106107
public List<String> nullableAnnotations;
107108
public boolean generateInfoJson;
108109
public boolean generateNpmPackageJson;
@@ -205,6 +206,7 @@ public void generate() throws Exception {
205206
settings.loadIncludePropertyAnnotations(classLoader, includePropertyAnnotations);
206207
settings.loadExcludePropertyAnnotations(classLoader, excludePropertyAnnotations);
207208
settings.loadOptionalAnnotations(classLoader, optionalAnnotations);
209+
settings.loadRequiredAnnotations(classLoader, requiredAnnotations);
208210
settings.loadNullableAnnotations(classLoader, nullableAnnotations);
209211
settings.generateInfoJson = generateInfoJson;
210212
settings.generateNpmPackageJson = generateNpmPackageJson;

typescript-generator-maven-plugin/src/main/java/cz/habarta/typescript/generator/maven/GenerateMojo.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ public class GenerateMojo extends AbstractMojo {
225225
* Specifies how properties are defined to be optional.
226226
* Supported values are:
227227
* <ul>
228-
* <li><code>useSpecifiedAnnotations</code> - annotations specified using {@link #optionalAnnotations} parameter</li>
228+
* <li><code>useSpecifiedAnnotations</code> - annotations specified using {@link #optionalAnnotations} or {@link #requiredAnnotations} parameter</li>
229229
* <li><code>useLibraryDefinition</code> - examples: <code>@JsonProperty(required = false)</code> when using <code>jackson2</code> library
230230
* or <code>@XmlElement(required = false)</code> when using <code>jaxb</code> library</li>
231231
* <li><code>all</code> - all properties are optional</li>
@@ -625,11 +625,19 @@ public class GenerateMojo extends AbstractMojo {
625625
* The presence of any annotation in this list on a JSON property will cause
626626
* the typescript-generator to treat that property as optional when generating
627627
* the corresponding TypeScript interface.
628-
* Example optional annotation: <code>javax.annotation.Nullable</code>
628+
* Example optional annotation: <code>javax.annotation.Nullable</code>.
629+
* This parameter is "opposite" of {@link #requiredAnnotations}, only one of them could be used.
629630
*/
630631
@Parameter
631632
private List<String> optionalAnnotations;
632633

634+
/**
635+
* Properties will be treated as optional except those annotated with any of specified annotations.
636+
* This parameter is "opposite" of {@link #optionalAnnotations}, only one of them could be used.
637+
*/
638+
@Parameter
639+
private List<String> requiredAnnotations;
640+
633641
/**
634642
* When any of specified annotations is used on a Java type typescript-generator treats this type as nullable.
635643
* For example Java type <code>List&lt;@Nullable String&gt;</code>
@@ -836,6 +844,7 @@ public void execute() {
836844
settings.loadIncludePropertyAnnotations(classLoader, includePropertyAnnotations);
837845
settings.loadExcludePropertyAnnotations(classLoader, excludePropertyAnnotations);
838846
settings.loadOptionalAnnotations(classLoader, optionalAnnotations);
847+
settings.loadRequiredAnnotations(classLoader, requiredAnnotations);
839848
settings.loadNullableAnnotations(classLoader, nullableAnnotations);
840849
settings.generateInfoJson = generateInfoJson;
841850
settings.generateNpmPackageJson = generateNpmPackageJson;

0 commit comments

Comments
 (0)