Skip to content

GH-49 Dynamic API#284

Draft
dzikoysk wants to merge 25 commits into
mainfrom
GH-49-openapi-dynamic
Draft

GH-49 Dynamic API#284
dzikoysk wants to merge 25 commits into
mainfrom
GH-49-openapi-dynamic

Conversation

@dzikoysk

@dzikoysk dzikoysk commented Jun 8, 2026

Copy link
Copy Markdown
Member

#49

val app = Javalin.create { config ->
    config.registerPlugin(
        OpenApiPlugin {
            // new hooks system (internal plugin system)
            it.withHook(RegisteredRoutesHook())
        }
    )
    
    // automatically detects registered route + method + path parameters
    config.routes.get("/users") { }
    config.routes.post("/users") { }

    // allows to attach more metadata
    config.routes.addEndpoint(
        Endpoint.create(HandlerType.GET, "/users/{id}")
            .addMetadata(OpenApiMetadata {
                summary("Get a user")
                responses {
                    response("200") {
                        description("The user")
                        content { mediaType("application/json") { schema(User::class.java) } }
                    }
                }
            })
            .handler { }
    )
}

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a backend-agnostic OpenAPI/JSON Schema generation pipeline (shared across annotation processing, KSP, and runtime reflection) and adds a runtime “hook” mechanism to let OpenApiPlugin enrich the served document with dynamically registered Javalin routes.

Changes:

  • Added a new introspection subsystem (API + runtime + JAP + KSP backends) to support schema generation from different compile/runtime environments.
  • Moved/rewired schema generation into openapi-generator so APT/Kapt, KSP, and reflection can share the same generator.
  • Added openapi-ksp (KSP processor), openapi-dynamic (reflection context), and a javalin-openapi-dynamic-hook plugin hook + docs/examples.

Reviewed changes

Copilot reviewed 95 out of 97 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
settings.gradle.kts Registers new modules for introspection, KSP, dynamic generation, and examples
openapi-specification/src/main/kotlin/io/javalin/openapi/OpenApiStatus.kt Adds HTTP status constants + reason phrase lookup used for default response descriptions
openapi-ksp/src/test/kotlin/io/javalin/openapi/ksp/OpenApiSymbolProcessorTest.kt KSP processor integration tests (schema + OpenAPI resource generation)
openapi-ksp/src/test/kotlin/io/javalin/openapi/ksp/KspSchemaContextTest.kt End-to-end KSP resolver-based schema generation test
openapi-ksp/src/test/kotlin/io/javalin/openapi/ksp/fixtures/User.kt KSP schema fixture type
openapi-ksp/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider Registers the KSP SymbolProcessorProvider
openapi-ksp/src/main/kotlin/io/javalin/openapi/ksp/OpenApiSymbolProcessor.kt Implements KSP-based OpenAPI + JSON schema resource generation
openapi-ksp/src/main/kotlin/io/javalin/openapi/ksp/KspSchemaContext.kt KSP-backed SchemaGenerationContext wiring
openapi-ksp/build.gradle.kts Adds dependencies for KSP backend and compile-testing
openapi-generator/src/test/kotlin/io/javalin/openapi/schema/OpenApiSchemaBuilderTest.kt Updates tests to use the new shared OpenApiType model
openapi-generator/src/main/kotlin/io/javalin/openapi/schema/OpenApiSchemaGenerator.kt New shared OpenAPI document generator (route-map input)
openapi-generator/src/main/kotlin/io/javalin/openapi/schema/OpenApiSchemaBuilder.kt Updates schema builder to use OpenApiType references
openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/SimpleType.kt Adds SimpleType model + opt-in annotation
openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/SchemaGenerationContext.kt Defines backend-agnostic schema generation interface
openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/processor/generators/TypeSchemaGenerator.kt Shared type→schema generator logic (now backend-agnostic)
openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/processor/generators/ResultScheme.kt Shared generator result model (json + references)
openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/processor/generators/PropertyComposition.kt Updates composition/discriminator model to use OpenApiType
openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/processor/generators/ExampleGenerator.kt Moves example mapping to Map-based representation
openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/processor/generators/CompositionGenerator.kt New shared composition generator (oneOf/anyOf/allOf + discriminator)
openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/OpenApiType.kt Renames/introduces OpenApiType model (with internal handle opt-in)
openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/IntrospectorSchemaContext.kt Base context for introspector-backed schema generation
openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/EmbeddedTypeProcessor.kt New embedded type processor API for pluggable schema handling
openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/defaults/DictionaryEmbeddedTypeProcessor.kt Fixes processor lookup to use embeddedTypeProcessors directly
openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/defaults/DefaultEmbeddedTypeProcessors.kt Provides default embedded type processor list factory
openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/defaults/CompositionEmbeddedTypeProcessor.kt Adds default processor to emit compositions
openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/defaults/ArrayEmbeddedTypeProcessor.kt Cleans unused import; participates in default processor set
openapi-generator/build.gradle.kts Adds introspection-api dependency to generator
openapi-dynamic/src/test/kotlin/io/javalin/openapi/dynamic/ReflectionSchemaContextTest.kt Reflection backend tests for schema generation behavior
openapi-dynamic/src/test/kotlin/io/javalin/openapi/dynamic/DynamicSchemaGeneratorTest.kt Tests component graph resolution through builder using reflection
openapi-dynamic/src/test/kotlin/io/javalin/openapi/dynamic/DynamicCompositionTest.kt Tests runtime composition behavior (incl. empty oneOf suppression)
openapi-dynamic/src/test/java/io/javalin/openapi/dynamic/fixtures/Validated.java Reflection fixtures for validation + redirect
openapi-dynamic/src/test/java/io/javalin/openapi/dynamic/fixtures/TransientDto.java Reflection fixtures for transient-field handling
openapi-dynamic/src/test/java/io/javalin/openapi/dynamic/fixtures/SnakeCaseDto.java Reflection fixtures for naming strategy
openapi-dynamic/src/test/java/io/javalin/openapi/dynamic/fixtures/Shape.java Reflection fixtures for composition annotations
openapi-dynamic/src/test/java/io/javalin/openapi/dynamic/fixtures/Role.java Reflection enum fixture for renamed constants
openapi-dynamic/src/test/java/io/javalin/openapi/dynamic/fixtures/NotNull.java Runtime-retained NotNull stand-in for name-based matching
openapi-dynamic/src/test/java/io/javalin/openapi/dynamic/fixtures/FieldsDto.java Reflection fixture for by-fields selection
openapi-dynamic/src/test/java/io/javalin/openapi/dynamic/fixtures/Dog.java Composition subtype fixture
openapi-dynamic/src/test/java/io/javalin/openapi/dynamic/fixtures/Cat.java Composition subtype fixture
openapi-dynamic/src/test/java/io/javalin/openapi/dynamic/fixtures/Address.java Reflection nested-object fixture
openapi-dynamic/src/test/java/io/javalin/openapi/dynamic/fixtures/Account.java Reflection feature-coverage fixture (ignore/rename/description/etc.)
openapi-dynamic/src/main/kotlin/io/javalin/openapi/dynamic/ReflectionSchemaContext.kt Reflection-backed schema context implementation
openapi-dynamic/build.gradle.kts Adds dependencies for reflection backend + tests
openapi-annotation-processor/src/test/kotlin/io/javalin/openapi/processor/ValidationAnnotationsTest.kt Adds tests for validation keyword emission
openapi-annotation-processor/src/test/kotlin/io/javalin/openapi/processor/PropertySelectionTest.kt Adds tests for ignore/by-fields selection behavior
openapi-annotation-processor/src/test/kotlin/io/javalin/openapi/processor/OperationAnnotationsTest.kt Adds tests for parameters/request body/security/responses emission
openapi-annotation-processor/src/test/compile/openapi.groovy Updates compile-time config script types/handles for OpenApiType
openapi-annotation-processor/src/main/kotlin/io/javalin/openapi/schema/OpenApiSchemaGenerator.kt Removes old AP-specific schema generator (replaced by shared one)
openapi-annotation-processor/src/main/kotlin/io/javalin/openapi/processor/generators/OpenApiGenerator.kt Switches annotation processor to shared generator and route-map model
openapi-annotation-processor/src/main/kotlin/io/javalin/openapi/processor/generators/JsonSchemaGenerator.kt Uses new toOpenApiType path for schema generation
openapi-annotation-processor/src/main/kotlin/io/javalin/openapi/experimental/processor/shared/ModelExtensions.kt Removes AP-only helpers now replaced by introspection APIs
openapi-annotation-processor/src/main/kotlin/io/javalin/openapi/experimental/processor/shared/AnnotationProcessorExtensions.kt Removes unused MessagerWriter utility
openapi-annotation-processor/src/main/kotlin/io/javalin/openapi/experimental/processor/generators/TypeSchemaGenerator.kt Removes AP-specific generator implementation (shared generator is used)
openapi-annotation-processor/src/main/kotlin/io/javalin/openapi/experimental/processor/generators/CompositionGenerator.kt Removes AP-specific composition generator (shared generator is used)
openapi-annotation-processor/src/main/kotlin/io/javalin/openapi/experimental/OpenApiTypeHandle.kt Adds OpenApiType handle utilities for AP backend
openapi-annotation-processor/src/main/kotlin/io/javalin/openapi/experimental/OpenApiAnnotationProcessorConfiguration.kt Updates configuration to use OpenApiType + shared embedded processor defaults
openapi-annotation-processor/src/main/kotlin/io/javalin/openapi/experimental/ClassDefinitionHandle.kt Removes old ClassDefinition handle utilities (replaced by OpenApiTypeHandle)
openapi-annotation-processor/src/main/kotlin/io/javalin/openapi/experimental/ClassDefinitionFactory.kt Removes old ClassDefinition factory (replaced by introspection/JAP backend)
openapi-annotation-processor/src/main/kotlin/io/javalin/openapi/experimental/AnnotationProcessorContext.kt Implements SchemaGenerationContext via JAP introspector and OpenApiType mapping
openapi-annotation-processor/build.gradle.kts Adds introspection-jap; removes direct Javalin dependency
javalin-plugins/javalin-openapi-plugin/src/main/kotlin/io/javalin/openapi/plugin/OpenApiPlugin.kt Adds per-version runtime hook application during doc creation
javalin-plugins/javalin-openapi-plugin/src/main/kotlin/io/javalin/openapi/plugin/OpenApiConfiguration.kt Introduces OpenApiHook/OpenApiHookContext + hook registration API
javalin-plugins/javalin-openapi-dynamic-hook/src/test/kotlin/io/javalin/openapi/dynamic/hook/DynamicOpenApiHookTest.kt Tests route auto-discovery + metadata overlay behavior
javalin-plugins/javalin-openapi-dynamic-hook/src/test/java/io/javalin/openapi/dynamic/hook/fixtures/User.java Fixture for metadata schema emission
javalin-plugins/javalin-openapi-dynamic-hook/src/main/kotlin/io/javalin/openapi/dynamic/hook/RegisteredRoutesHook.kt Implements runtime route enumeration and OpenAPI document extension
javalin-plugins/javalin-openapi-dynamic-hook/src/main/kotlin/io/javalin/openapi/dynamic/hook/OpenApiMetadata.kt Adds metadata container + schema(Class) helper for route enrichment
javalin-plugins/javalin-openapi-dynamic-hook/build.gradle.kts Adds module wiring + runtime/test dependencies
introspection/introspection-test/src/test/kotlin/io/javalin/introspection/test/IntrospectionParityTest.kt Adds parity tests across runtime and annotation-processing backends
introspection/introspection-test/src/test/kotlin/io/javalin/introspection/test/fixtures/Fixtures.kt Test fixture types for introspection parity
introspection/introspection-test/src/test/kotlin/io/javalin/introspection/test/AnnotationProcessing.kt Utility to run JAP backend inside in-process javac
introspection/introspection-test/src/test/java/io/javalin/introspection/test/sub/PackagePrivateBase.java Fixture for cross-package inheritance behavior
introspection/introspection-test/src/test/java/io/javalin/introspection/test/Note.java Fixture for repeatable annotation behavior
introspection/introspection-test/build.gradle.kts Adds dependencies for parity tests
introspection/introspection-runtime/src/test/kotlin/io/javalin/introspection/runtime/ReflectionTypeIntrospectorTest.kt Tests runtime reflection introspector behavior
introspection/introspection-runtime/src/main/kotlin/io/javalin/introspection/runtime/ReflectionTypeIntrospector.kt Implements reflection TypeIntrospector backend
introspection/introspection-runtime/build.gradle.kts Runtime introspector module build config
introspection/introspection-ksp/src/test/kotlin/io/javalin/introspection/ksp/KspTypeIntrospectorTest.kt Tests KSP introspector behavior via compile-testing
introspection/introspection-ksp/src/test/kotlin/io/javalin/introspection/ksp/fixtures/Fixtures.kt KSP fixture types
introspection/introspection-ksp/src/main/kotlin/io/javalin/introspection/ksp/KspTypeIntrospector.kt Implements KSP TypeIntrospector backend
introspection/introspection-ksp/build.gradle.kts KSP introspector module build config
introspection/introspection-jap/src/main/kotlin/io/javalin/introspection/jap/JapTypeIntrospector.kt Implements JAP (javax.lang.model) TypeIntrospector backend
introspection/introspection-jap/build.gradle.kts JAP introspector module build config
introspection/introspection-api/src/main/kotlin/io/javalin/introspection/TypeIntrospector.kt Introduces TypeIntrospector abstraction
introspection/introspection-api/src/main/kotlin/io/javalin/introspection/PropertyView.kt Adds PropertyView + accessor/visibility + getter-name utilities
introspection/introspection-api/src/main/kotlin/io/javalin/introspection/InternalIntrospectionApi.kt Adds opt-in guard for backend-native source tokens
introspection/introspection-api/src/main/kotlin/io/javalin/introspection/CompileTimeIntrospector.kt Adds compile-time extension (typesAnnotatedWith)
introspection/introspection-api/src/main/kotlin/io/javalin/introspection/ClassDefinition.kt Introduces backend-agnostic type model + enum/property access
introspection/introspection-api/src/main/kotlin/io/javalin/introspection/Annotations.kt Adds name/value-based annotation access + meta-annotation support
introspection/introspection-api/build.gradle.kts Introspection API module build config
gradle/libs.versions.toml Adds KSP + compile-testing dependencies and KSP Gradle plugin
examples/javalin-ksp-kotlin/src/main/resources/logback.xml Example logging config
examples/javalin-ksp-kotlin/src/main/kotlin/io/javalin/openapi/ksp/example/App.kt Adds example Kotlin app using KSP backend
examples/javalin-ksp-kotlin/build.gradle.kts Adds KSP plugin config + ksp args for example
docs/introduction/setup.md Documents Gradle KSP setup + backend selection guidance
docs/introduction/json-schema-setup.md Documents KSP-based JsonSchema generation setup
docs/advanced/runtime-builder.md Documents runtime hook module and metadata enrichment
docs/advanced/configuration.md Documents KSP args + clarifies APT-only configuration features
Comments suppressed due to low confidence (1)

openapi-generator/src/main/kotlin/io/javalin/openapi/experimental/processor/generators/ResultScheme.kt:17

  • schema.put($$"$schema", ...) is invalid Kotlin syntax and will not compile. Use "\$schema" to emit the JSON Schema $schema keyword.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread openapi-specification/src/main/kotlin/io/javalin/openapi/OpenApiStatus.kt Outdated
# Conflicts:
#	openapi-annotation-processor/src/main/kotlin/io/javalin/openapi/experimental/processor/generators/TypeSchemaGenerator.kt
@lukashoegh

Copy link
Copy Markdown

Oooh you're working on supporting ksp? Thats awesome!

@dzikoysk

Copy link
Copy Markdown
Member Author

@lukashoegh well, this PR introduces a lot of things at once 😅 but yeah, ksp seems to be working just fine already:

Let's see if I will manage to finish it, initially I was supposed to only provide the runtime (reflection based) backend, but to do it properly we should target all known possible targets as soon as possible to make sure that the abstraction layer does the trick 😬

@dzikoysk dzikoysk force-pushed the GH-49-openapi-dynamic branch from 4cc4a20 to 5bd3d1a Compare June 28, 2026 12:47
@dzikoysk dzikoysk force-pushed the GH-49-openapi-dynamic branch from 5bd3d1a to 2f05318 Compare June 28, 2026 13:07
@dzikoysk dzikoysk requested a review from Copilot June 28, 2026 13:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants