From 019f6d4505946ff3ee7ad011cf03bc1fc492921f Mon Sep 17 00:00:00 2001 From: lpandzic Date: Tue, 28 Jan 2025 13:58:21 +0100 Subject: [PATCH] added support for InterfaceRecordPropertiesAnnotationIntrospector which automatically discovers record components on sealed interfaces --- .../OrderedTypescriptGenerator.java | 16 +++++++ .../typescript/TypeScriptFileGenerator.java | 3 -- ...ecordPropertiesAnnotationIntrospector.java | 37 ++++++++++++++ ...nterfaceRecordPropertyDiscoveryModule.java | 12 +++++ .../InterfaceRecordPropertyDiscoveryTest.java | 48 +++++++++++++++++++ 5 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/record/property/discovery/InterfaceRecordPropertiesAnnotationIntrospector.java create mode 100644 infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/record/property/discovery/InterfaceRecordPropertyDiscoveryModule.java create mode 100644 infobip-typescript-generator-extension-api/src/test/java/com/infobip/typescript/record/property/discovery/InterfaceRecordPropertyDiscoveryTest.java diff --git a/infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/OrderedTypescriptGenerator.java b/infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/OrderedTypescriptGenerator.java index 85ce1e7..d691351 100644 --- a/infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/OrderedTypescriptGenerator.java +++ b/infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/OrderedTypescriptGenerator.java @@ -1,13 +1,29 @@ package com.infobip.typescript; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.infobip.typescript.record.property.discovery.InterfaceRecordPropertyDiscoveryModule; import cz.habarta.typescript.generator.Input; import cz.habarta.typescript.generator.TypeScriptGenerator; +import cz.habarta.typescript.generator.parser.Jackson2Parser; public class OrderedTypescriptGenerator { final TypeScriptGenerator generator; public OrderedTypescriptGenerator(TypeScriptGenerator generator) { this.generator = generator; + var typeScriptGeneratorObjectMapper = getObjectMapper(generator); + typeScriptGeneratorObjectMapper.registerModule(new InterfaceRecordPropertyDiscoveryModule()); + } + + private ObjectMapper getObjectMapper(TypeScriptGenerator generator) { + var modelParser = (Jackson2Parser) generator.getModelParser(); + try { + var field = Jackson2Parser.class.getDeclaredField("objectMapper"); + field.setAccessible(true); + return (ObjectMapper) field.get(modelParser); + } catch (Exception e) { + throw new RuntimeException(e); + } } public String generateTypeScript(Input input) { diff --git a/infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/TypeScriptFileGenerator.java b/infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/TypeScriptFileGenerator.java index 243aeb8..cfad399 100644 --- a/infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/TypeScriptFileGenerator.java +++ b/infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/TypeScriptFileGenerator.java @@ -10,7 +10,6 @@ import cz.habarta.typescript.generator.emitter.EmitterExtension; import cz.habarta.typescript.generator.emitter.TsModel; import cz.habarta.typescript.generator.parser.Model; -import cz.habarta.typescript.generator.util.Utils; import java.io.*; import java.lang.annotation.Annotation; @@ -37,8 +36,6 @@ protected TypeScriptFileGenerator(Path basePath) { } public void generate() { - var objectMapper = Utils.getObjectMapper(); - objectMapper.findAndRegisterModules(); List extensions = createExtensions(); Settings settings = createSettings(extensions); OrderedTypescriptGenerator generator = createGenerator(settings); diff --git a/infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/record/property/discovery/InterfaceRecordPropertiesAnnotationIntrospector.java b/infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/record/property/discovery/InterfaceRecordPropertiesAnnotationIntrospector.java new file mode 100644 index 0000000..8ff5327 --- /dev/null +++ b/infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/record/property/discovery/InterfaceRecordPropertiesAnnotationIntrospector.java @@ -0,0 +1,37 @@ +package com.infobip.typescript.record.property.discovery; + +import com.fasterxml.jackson.databind.PropertyName; +import com.fasterxml.jackson.databind.introspect.*; + +import java.util.stream.Stream; + +public class InterfaceRecordPropertiesAnnotationIntrospector extends NopAnnotationIntrospector { + + @Override + public PropertyName findNameForSerialization(Annotated a) { + if(!(a instanceof AnnotatedMember annotatedMember)) { + return super.findNameForSerialization(a); + } + + Class declaringClass = annotatedMember.getDeclaringClass(); + + if(!declaringClass.isInterface()) { + return super.findNameForSerialization(a); + } + + if(!declaringClass.isSealed()) { + return super.findNameForSerialization(a); + } + + var hasRecordGetter = Stream.of(declaringClass.getPermittedSubclasses()) + .filter(Class::isRecord) + .flatMap(subclass -> Stream.of(subclass.getRecordComponents())) + .anyMatch(component -> component.getName().equals(a.getName())); + + if(!hasRecordGetter) { + return super.findNameForSerialization(a); + } + + return new PropertyName(a.getName()); + } +} diff --git a/infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/record/property/discovery/InterfaceRecordPropertyDiscoveryModule.java b/infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/record/property/discovery/InterfaceRecordPropertyDiscoveryModule.java new file mode 100644 index 0000000..901f1ea --- /dev/null +++ b/infobip-typescript-generator-extension-api/src/main/java/com/infobip/typescript/record/property/discovery/InterfaceRecordPropertyDiscoveryModule.java @@ -0,0 +1,12 @@ +package com.infobip.typescript.record.property.discovery; + +import com.fasterxml.jackson.databind.module.SimpleModule; + +public class InterfaceRecordPropertyDiscoveryModule extends SimpleModule { + + @Override + public void setupModule(SetupContext context) { + super.setupModule(context); + context.insertAnnotationIntrospector(new InterfaceRecordPropertiesAnnotationIntrospector()); + } +} diff --git a/infobip-typescript-generator-extension-api/src/test/java/com/infobip/typescript/record/property/discovery/InterfaceRecordPropertyDiscoveryTest.java b/infobip-typescript-generator-extension-api/src/test/java/com/infobip/typescript/record/property/discovery/InterfaceRecordPropertyDiscoveryTest.java new file mode 100644 index 0000000..7175095 --- /dev/null +++ b/infobip-typescript-generator-extension-api/src/test/java/com/infobip/typescript/record/property/discovery/InterfaceRecordPropertyDiscoveryTest.java @@ -0,0 +1,48 @@ +package com.infobip.typescript.record.property.discovery; + +import com.infobip.typescript.TestBase; +import com.infobip.typescript.type.JsonTypeExtension; +import cz.habarta.typescript.generator.Input; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.stream.Stream; + +import static org.assertj.core.api.BDDAssertions.then; + +class InterfaceRecordPropertyDiscoveryTest extends TestBase { + + InterfaceRecordPropertyDiscoveryTest() { + super(new JsonTypeExtension(Stream::empty), + Collections.emptyList()); + } + + @Test + void shouldAddReadonlyTypeField() { + + // when + String actual = whenGenerate(Input.from(HierarchyRoot.class, + FirstLeaf.class)); + + // then + then(actual).isEqualTo( + """ + + export interface HierarchyRoot { + value: string; + } + + export class FirstLeaf implements HierarchyRoot { + value: string; + } + """); + } + + sealed interface HierarchyRoot permits FirstLeaf { +// @JsonGetter + String value(); + } + + record FirstLeaf(String value) implements HierarchyRoot { + } +}