Skip to content

[JAVA-SPRING;KOTLIN-SPRING] feature: x-field-extra-annotation parity for kotlin-spring params + new x-request-body-extra-annotation + --inject-operation-vendor-extensions - #24780

Draft
Picazsoo wants to merge 34 commits into
OpenAPITools:masterfrom
Picazsoo:feature/add-extra-annotation-to-request-body-param
Draft

Picazsoo wants to merge 34 commits into
OpenAPITools:masterfrom
Picazsoo:feature/add-extra-annotation-to-request-body-param

Conversation

@Picazsoo

@Picazsoo Picazsoo commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds custom-annotation support for previously uncovered spots in the Spring generators:

  1. x-field-extra-annotation on kotlin-spring path/query/form/header params: brings kotlin-spring
    to parity with java-spring, which already renders this extension on parameters.
  2. New operation-level x-request-body-extra-annotation (java-spring and kotlin-spring):
    lets you attach annotations to the generated request-body parameter, even when the body $refs a
    shared model.
  3. x-field-extra-annotation on cookie params (java-spring and kotlin-spring): closes the
    last parameter kind that supported it in neither generator.
  4. New --inject-operation-vendor-extensions CLI/config flag (all generators): injects vendor
    extensions onto operations and their parameters from the command line or config, without editing
    the spec. This complements the existing --inject-model-vendor-extensions flag and is the natural
    way to apply the two extensions above when you cannot or do not want to touch the source contract.

Both accept a string or a list of strings, are applied per operation (selective), and are a
no-op when absent.

Motivation

Users commonly reference reusable ID/model schemas from many operations:

components:
  schemas:
    OrgId: { type: string, format: uuid }
    Employee: { ... }

To add a validation/framework annotation to a specific usage (e.g. Hibernate Validator / LSP rules
that require constraints to live on the generated interface, not the impl), you need a placement that
is both ref-safe and per-operation:

  • Path / query / form / header / cookie params support two placements, and you can pick based
    on the scope you want:

    • On the shared schema (for example on OrgId): applies globally to every parameter that
      $refs that schema. Use this when the annotation should always accompany the type. The generator
      already merges a referenced parameter schema's extensions onto the parameter, so this works for
      the simple alias schemas typically used by parameters.
    • On the Parameter Object (sibling of name/in): applies selectively to that single
      usage, and keeps the shared schema: { $ref: ... } clean and reusable for other operations. Use
      this when only some usages should carry the annotation.
    • Previously java-spring rendered the annotation on path/query/form/header params but not cookie,
      while kotlin-spring rendered it on none of them. Parts 1 and 3 close those gaps so every parameter
      kind behaves consistently in both generators.
  • Request bodies usually $ref a shared model, so there is no per-usage object to annotate:

    • keys placed next to a $ref are ignored (OpenAPI 3.0/3.1),
    • annotating the referenced model applies globally to every operation.

    Part 2 solves this with an operation-level extension: operations are never $ref targets, so
    the annotation is inherently per-usage and ref-safe.

Example (Part 1: parameter object)

parameters:
  - name: orgId
    in: path
    required: true
    x-field-extra-annotation: "@com.example.ValidOrgId"   # on the parameter object
    schema:
      $ref: '#/components/schemas/OrgId'                   # ref stays clean/reusable

Example (Part 2: request body)

paths:
  /employees:
    post:
      x-request-body-extra-annotation: "@com.example.MyValidation"
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Employee'

Generates (java-spring):

ResponseEntity<Void> createEmployee(
    @com.example.MyValidation @Valid @RequestBody Employee employee
) { ... }

Request-body annotation placements (three working scopes)

Besides the new operation-level extension, the body param also honors annotations declared on the
RequestBody Object itself (these are merged onto the generated body parameter). This gives three
placements, chosen by the scope you want:

  1. Operation-level x-request-body-extra-annotation (the new extension): per operation. Works
    even when the operation's requestBody is a bare $ref.
  2. Inline requestBody object x-field-extra-annotation: per operation.
    requestBody:
      x-field-extra-annotation: "@com.example.InlineBodyValidation"
      content: { application/json: { schema: { $ref: '#/components/schemas/Employee' } } }
  3. Reusable components.requestBodies object x-field-extra-annotation: applies to every
    operation that $refs that reusable request body (a shared subset).
    components:
      requestBodies:
        AnnotatedEmployeeBody:
          x-field-extra-annotation: "@com.example.ReusableBodyValidation"
          content: { application/json: { schema: { $ref: '#/components/schemas/Employee' } } }

Caveat: a key placed as a sibling of $ref in the operation's requestBody is ignored (OpenAPI
3.0/3.1), so to annotate a $ref-ed reusable body the extension must live inside the reusable
components.requestBodies object (option 3), not next to the $ref.

Example (injecting the extensions from the CLI, no spec edit)

When you cannot modify the source contract, the same result is achievable via
--inject-operation-vendor-extensions:

openapi-generator generate -g spring -i api.yaml -o out \
  --inject-operation-vendor-extensions \
      "createEmployee.x-request-body-extra-annotation=@com.example.MyValidation" \
  --inject-operation-vendor-extensions \
      "createEmployee.orgId.x-field-extra-annotation=@com.example.ValidOrgId"

Key formats:

  • operationId.x-extension-name=value targets the operation (for example the request-body
    extension above).
  • operationId.paramBaseName.x-extension-name=value targets a parameter, matched by its spec
    name (baseName). The operationId segment is matched against the spec-authored operationId when
    present, falling back to the generated operationId only when the spec omits one.

Changes

New extension & wiring

  • VendorExtension: added X_REQUEST_BODY_EXTRA_ANNOTATION (OPERATION level).
  • Registered in getSupportedVendorExtensions() for both spring and kotlin-spring.
  • DefaultCodegen: new shared helper mergeOperationVendorExtensionIntoBodyParams(...) that copies
    the operation-level values onto the body parameter's x-field-extra-annotation list, so both
    extensions render through the existing body-param template path.
  • Normalization (string-or-list → List<String>) applied in both SpringCodegen and
    KotlinSpringServerCodegen (the latter gains a small parameter-normalization helper, since it does
    not share the Java generator's base class).

Vendor-extension injection flag (all generators)

  • New --inject-operation-vendor-extensions CLI option (repeatable) plus the matching
    CodegenConfigurator.addInjectOperationVendorExtension / GeneratorSettings wiring, mirroring the
    existing --inject-model-vendor-extensions plumbing.
  • Injection is applied in DefaultCodegen.fromOperation, so it runs for every generator and before
    generator-specific post-processing. This means an injected x-request-body-extra-annotation flows
    through the same normalize-and-merge path as one declared in the spec.

Templates

  • kotlin-spring pathParams / queryParams / formParams / headerParams: render
    x-field-extra-annotation before the parameter binding (java-spring parity).
  • java-spring & kotlin-spring cookieParams: render x-field-extra-annotation before @CookieValue
    / the cookie binding (previously unsupported in both).
  • java-spring & kotlin-spring bodyParams: render the merged annotation before @RequestBody /
    the body binding. Covers useOptional and reactive (Mono/Flux, Flow/suspend) variants.
  • Header and cookie params required no generator-code change: both generators already normalize those
    parameter collections to List<String>, so only the templates were missing.

Samples (compile-verified)

  • To exercise the features in real, compiled samples without adding slow new build targets, the two
    shared petstore specs were copied and the copies annotated:
    petstore-with-fake-endpoints-models-for-testing-extra-annotation.yaml (java) and
    3_0/kotlin/petstore-with-extra-annotation.yaml (kotlin). The originals are untouched, so there is
    no ripple to the 140+ other configs that consume them.
  • Four existing samples were repointed to the copies, covering both generators across reactive and
    non-reactive: springboot-useoptional (java, useOptional), springboot-reactive (java,
    Mono/Flux), kotlin-springboot-delegate (kotlin), and kotlin-springboot-reactive (kotlin,
    Flow/suspend). All four compile locally.
  • The annotated copies demonstrate: operation-level x-request-body-extra-annotation on addPet
    (a $ref body) with updatePet left un-annotated (selectivity); param x-field-extra-annotation
    on a path, a list-valued query, and a form param; and, on the kotlin copy, x-extra-imports so the
    annotations are referenced by short name.
  • The same four samples also exercise the --inject-operation-vendor-extensions side-loading path
    (expressed as an injectOperationVendorExtensions: block in each sample config, the config-file
    equivalent of the CLI flag). Without editing the spec, they inject an operation-level
    x-request-body-extra-annotation onto placeOrder and a parameter-level x-field-extra-annotation
    onto the getOrderById path param. Because the java base spec names that param order_id while the
    kotlin base spec names it orderId, the two configs use different keys, which validates that the
    parameter segment is matched against the raw spec paramBaseName. On the kotlin samples the configs
    also side-load x-extra-imports next to the injected annotations, so the injected @NonNull is
    imported and referenced by short name (the java samples keep the fully-qualified form, since
    java-spring has no x-extra-imports support). The injected annotations render and compile alongside
    the spec-declared ones.

Docs

  • Regenerated docs/generators/spring.md and docs/generators/kotlin-spring.md (vendor-extension
    tables) via the docs task, not hand-edited. (No table change for the header/cookie additions, which
    reuse the existing x-field-extra-annotation.)

Tests

  • java-spring: request-body annotation with a $ref body + a second operation on the same model
    without the extension (proves selectivity); covers a non-default body branch. Cookie-param
    coverage added to the existing parameter-annotation test.
  • kotlin-spring: x-field-extra-annotation on path/query/form/header/cookie params (incl.
    list-valued cases), plus request-body annotation with selectivity.
  • injection flag: unit test in DefaultCodegenTest (operation-level and parameter-level landing,
    non-matching operationId is a no-op) plus java-spring and kotlin-spring end-to-end tests that inject
    both extensions and assert selective rendering.
  • request-body placements: the java-spring and kotlin-spring request-body tests also assert that an
    x-field-extra-annotation declared on the inline requestBody object and on a reusable
    components.requestBodies object (referenced by two operations) render on the body param.

Design notes / trade-offs

  • Merge into x-field-extra-annotation instead of a new template branch. The operation-level
    request-body value is merged onto the body parameter's existing annotation list, so rendering
    reuses one code path and multiple annotations compose naturally. Existing param-level annotations
    are preserved and appear first.
  • Values normalized to List<String>. A single string and a list are handled uniformly, extra
    values can be appended, and an absent extension yields an empty list → renders nothing (no change
    when unused).
  • The param extension supports both schema-level and Parameter Object placement. Placing it on
    the shared schema applies it globally to every usage of that schema; placing it on the Parameter
    Object applies it selectively to a single usage and keeps the shared $ref reusable. Both are
    valid; choose based on whether you want global or per-usage scope.

Known limitation (out of scope)

If a requestBody declares different schemas per content type, the generator still models it as a
single body parameter / single @RequestBody argument (Spring itself binds one body per method),
so the annotation applies to that one binding, and it cannot be varied per content type. Generating one
method per content type (via consumes dispatch) would be the fully spec-faithful approach but is a
broader change (it breaks the 1:1 operation→method contract across all generators) and is not
addressed here. The operation-level design does not preclude adding a media-type-level extension
later if that path is ever taken.

Acceptance

  • kotlin-spring renders x-field-extra-annotation on path/query/form/header params (java-spring
    parity), including list values; model-field behavior unchanged.
  • x-field-extra-annotation renders on cookie params in both java-spring and kotlin-spring.
  • x-request-body-extra-annotation renders before the body param in java-spring and kotlin-spring,
    works with $ref bodies, is selective per operation, and covers useOptional / reactive variants.
  • Both extensions accept a string or a list of strings; absent extension = no change.
  • --inject-operation-vendor-extensions applies operation- and parameter-level extensions from the
    CLI/config for any generator, selectively per operation, with an empty/absent map being a no-op.
  • Docs regenerated; new tests pass with no regressions.
  • Four existing samples (java + kotlin, reactive + non-reactive) were repointed to annotated copies
    of the shared petstore specs and compile with the emitted annotations, verifying the feature
    end-to-end without adding new build targets. The same samples also inject both extensions via an
    injectOperationVendorExtensions: config block (the side-loading path), confirming the CLI/config
    injection mechanism renders and compiles too.

Plugin parity (Gradle + Maven)

The injectModelVendorExtensions / injectOperationVendorExtensions side-loading maps were only
reachable from the plugins through a configFile. They are now first-class properties on both the
Gradle and Maven plugins, matching the CLI and the existing parity of the other mapping properties
(nameMappings, globalProperties, etc.):

  • Gradle (openApiGenerate { ... }): two MapProperty<String, String> fields wired through the
    extension, plugin, and GenerateTask to the configurator.
  • Maven (<configuration>): two List<String> KVP parameters
    (openapi.generator.maven.plugin.inject{Model,Operation}VendorExtensions) applied via the shared
    applyInject*KvpList helpers. These settings are configurator-level (not generator CliOptions)
    and have no configOptions backwards-compat reader, so unlike the legacy mapping options they need
    no configOptions guard; the same review also removed the equally dead configOptions guards from
    the five *-name-mappings parameters (the inline-schema-options guard is kept, since it does have
    a compat reader).
  • Docs: added to the Gradle README.adoc and Maven README.md config tables. The CLI help for
    --inject-*-vendor-extensions now clarifies that multiple annotations in a single value are
    space-separated (emitted verbatim into source); an unquoted comma is the separator between
    different injection targets, so comma-bearing annotation attributes should use the spec-level
    extension (string or YAML list) instead.
  • Tests: a Gradle ParameterWiringRegressionTest case asserts the injected annotation reaches
    generated sources, and a Maven CodeGenMojoTest case (new inject-vendor-extensions resource
    project) asserts the injected x-request-body-extra-annotation is merged onto the body parameter
    and rendered on the generated Spring API.

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

Picazsoo and others added 5 commits August 26, 2026 09:58
…d parameter vendor extensions

Injects vendor extensions onto operations and their parameters from the CLI or config without editing the spec, complementing --inject-model-vendor-extensions. Applied in DefaultCodegen.fromOperation so it works for all generators and flows through Spring's request-body annotation normalization.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Picazsoo Picazsoo changed the title [JAVA-SPRING;KOTLIN-SPRING] feature: 'x-field-extra-annotation' parity for kotlin-spring params + new 'x-request-body-extra-annotation' [JAVA-SPRING;KOTLIN-SPRING] feature: x-field-extra-annotation parity for kotlin-spring params + new x-request-body-extra-annotation + --inject-operation-vendor-extensions Aug 26, 2026
…ation placements

Adds regression coverage proving x-field-extra-annotation declared on the inline requestBody object and on a reusable components.requestBodies object renders on the generated body parameter in java-spring and kotlin-spring.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Picazsoo
Picazsoo marked this pull request as ready for review August 26, 2026 11:31
@Picazsoo
Picazsoo marked this pull request as draft August 26, 2026 11:31

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 27 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

- GeneratorSettings: include injectModelVendorExtensions and
  injectOperationVendorExtensions in equals() and hashCode() so configs
  differing only in these maps are no longer treated as equal.
- JavaCamelServerCodegen: stop advertising x-request-body-extra-annotation,
  which its Camel REST DSL templates never render; regenerate java-camel docs.
- DefaultCodegen: move the shared parameter vendor-extension normalization
  (normalizeOperationParameterVendorExtensions) up from AbstractJavaCodegen and
  reuse it from KotlinSpringServerCodegen, removing the duplicate helpers.
- DefaultCodegen.injectOperationVendorExtensions: match the spec-authored
  operationId (operationIdOriginal) when present, falling back to the generated
  operationId only when the spec omits one.
- Correct docs/help to describe the parameter key segment as the spec name
  (paramBaseName / baseName) to match the actual matching logic.
- Tests: add snake_case operationId injection test and a JavaCamel supported
  vendor-extension test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 29 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Picazsoo and others added 10 commits August 26, 2026 14:37
- DefaultCodegen.injectOperationVendorExtensions: treat a blank
  operationIdOriginal like a missing one and fall back to the generated
  operationId, so injection is not silently skipped when the spec declares an
  empty operationId.
- DefaultCodegenTest: use expected-first argument order for JUnit assertEquals
  and add a regression test covering the blank-operationId fallback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
op.operationId is always non-blank at injection time because
getOrGenerateOperationId synthesizes one from the path and HTTP method when
the spec omits or blanks it. Replace the unreachable isBlank(matchOperationId)
early-return with an explicit Objects.requireNonNull on op.operationId so the
invariant is documented and a future regression fails loudly instead of
silently dropping injected extensions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mples

Add compile coverage for the Spring extra-annotation features by copying the
shared petstore specs, adding the extension annotations to the copies, and
repointing four existing (already-compiled) samples to them. The originals are
left untouched, so there is no ripple to the 140+ other configs and no new
build target.

Copied specs (originals unchanged):
- 3_0/spring/petstore-with-fake-endpoints-models-for-testing-extra-annotation.yaml
- 3_0/kotlin/petstore-with-extra-annotation.yaml

Repointed samples (cover java/kotlin x reactive/non-reactive):
- springboot-useoptional (java, useOptional body branch)
- springboot-reactive (java, Mono/Flux body branch)
- kotlin-springboot-delegate (kotlin, non-reactive)
- kotlin-springboot-reactive (kotlin, Flow/suspend body branch)

Exercised, all verified to compile locally (mvn + gradle):
- operation-level x-request-body-extra-annotation on addPet (body is a $ref),
  with updatePet left un-annotated to prove per-operation selectivity
- param x-field-extra-annotation on path (getPetById), list-valued query
  (findPetsByStatus, two annotations), and form (uploadFile) params
- kotlin references short names imported via x-extra-imports; java uses
  fully-qualified Spring @nonnull (java-spring has no x-extra-imports support)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The x-field-extra-annotation section in JavaSpring/bodyParams.mustache emitted
the annotation with a trailing space and no leading space. Because it sits
directly after {{>paramDoc}} (which ends in ")" with no trailing space), the
result glued the annotation to the @parameter(...) close paren and produced a
double space before @Valid, e.g.

    ...required = true)@org.springframework.lang.NonNull  @Valid @RequestBody

Switch to a leading-space style (matching the surrounding binding annotations)
so the output is now:

    ...required = true) @org.springframework.lang.NonNull @Valid @RequestBody

Only java-spring was affected; the kotlin-spring @parameter block already ends
with a trailing space, so its output was already correct. The section renders
nothing when the extension is absent, so no other samples change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mples

Extend the four repointed samples so they also cover the side-loading path: an
injectOperationVendorExtensions: block in each sample config (the config-file
equivalent of the --inject-operation-vendor-extensions CLI flag) injects the
extensions without editing the spec.

Injected onto store operations (kept separate from the pet operations used for
the spec-declared demo):
- placeOrder: operation-level x-request-body-extra-annotation
- getOrderById: parameter-level x-field-extra-annotation on the path param

The java base spec names that path param order_id while the kotlin base spec
names it orderId, so the two configs use different keys. This validates that the
parameter segment is matched against the raw spec paramBaseName. Values use the
fully-qualified @org.springframework.lang.NonNull, so the injected demo needs no
imports and compiles on its own.

Regenerated StoreApi for all four samples (java + kotlin, reactive +
non-reactive); the injected annotations render before the placeOrder body
binding (incl. Mono<Order> in the java reactive sample) and before the
getOrderById path param. All four samples compile.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…otations

The kotlin generator collects x-extra-imports from operation and parameter
vendor extensions, and those extensions can themselves be side-loaded. Inject
x-extra-imports next to the injected annotations on the two kotlin samples so the
injected annotation can use the short name instead of a fully-qualified one:

  placeOrder.x-request-body-extra-annotation: "@nonnull"
  placeOrder.x-extra-imports: org.springframework.lang.NonNull
  getOrderById.orderId.x-field-extra-annotation: "@nonnull"
  getOrderById.orderId.x-extra-imports: org.springframework.lang.NonNull

Regenerated StoreApi for both kotlin samples: the injected import is added to the
file and the short @nonnull renders on both the placeOrder body and the
getOrderById path param. Both samples compile. The java samples keep the
fully-qualified form, since java-spring has no x-extra-imports support.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bring CLI/plugin parity for the vendor-extension side-loading feature by
exposing injectModelVendorExtensions and injectOperationVendorExtensions on
both the Gradle and Maven plugins (previously only reachable via a configFile).

- Gradle plugin: new mapProperty extension fields, plugin wiring, and the four
  GenerateTask mirror points (WorkParameters, execute, task inputs, parameters).
- Maven plugin: two List<String> KVP @parameter fields with guarded
  applyInject*KvpList calls.
- Docs: Gradle README.adoc and Maven README.md config tables; CLI help now
  clarifies that multiple annotations in a single value are space-separated,
  since an unquoted comma separates different injection targets.
- Tests: Gradle ParameterWiringRegressionTest wiring test and Maven
  CodeGenMojoTest inject-vendor-extensions resource project asserting the
  injected request-body annotation renders on the generated Spring API.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Clarify in the Gradle plugin README that multiple annotations in a single
injected value are space-separated (emitted verbatim, safe inside parentheses),
with a groovy example. Note that commas inside a value need no escaping in the
Gradle map form, unlike the comma-separated CLI/Maven KVP form.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…anism

Reword the Gradle/Maven/CLI docs to describe injectModelVendorExtensions and
injectOperationVendorExtensions as a generic vendor-extension mechanism: values
are strings, applied at render time, and overwrite existing values; missing
targets are a silent no-op. The space-vs-comma guidance is scoped to the
extra-annotation extensions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Picazsoo
Picazsoo marked this pull request as ready for review August 26, 2026 15:42
@Picazsoo
Picazsoo marked this pull request as draft August 26, 2026 15:42
@Picazsoo

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai, please re-review

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai, please re-review

@Picazsoo I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 56 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 56 files

Re-trigger cubic

Picazsoo and others added 2 commits August 26, 2026 19:24
Strengthen testInjectOperationVendorExtensions so it no longer passes on a mere
substring match anywhere in the generated sources. It now locates PetApi.java,
asserts the injected @com.example.MyValidation sits on addPet's @RequestBody body
parameter, asserts a control operation (updatePet, which also has a body but no
injection) does not receive it, and asserts the annotation appears exactly once.
This guards the operation-scoped merge against non-selective or wrong-target
regressions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rams

The inject-model/operation-vendor-extensions settings are top-level
configurator options, not per-generator CliOptions, so a key placed in
<configOptions> is never forwarded (CodeGenMojo only forwards keys matching
config.cliOptions(), plus SOURCE_FOLDER). The guard therefore protected
against an unreachable double-application. Simplify to a plain null check.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 56 files

Re-trigger cubic

@wing328

wing328 commented Sep 20, 2026

Copy link
Copy Markdown
Member

please resolve the merge conflicts when you've time and i'll try to get it merged before the upcoming release.

…dy-param

# Conflicts:
#	modules/openapi-generator/src/main/resources/JavaSpring/bodyParams.mustache
#	samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/api/PetApi.java
#	samples/server/petstore/springboot-useoptional/src/main/java/org/openapitools/api/PetApi.java

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 56 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Picazsoo and others added 6 commits September 21, 2026 21:23
- Fix stale copyright year in inject-vendor-extensions/pom.xml test resource
- Fix injectOperationVendorExtensions timing: apply operation-level
  injections right after operationId is resolved, and parameter-level
  injections before each postProcessParameter call, so consumers see
  injected values in time
- Fix dotted-operationId matching by prefix-matching the operationId
  instead of blindly splitting the key on '.'
- Add ordering assertion in CodeGenMojoTest to verify injected annotation
  renders before @RequestBody
- Replace contradictory @NonNull/@nullable annotation pair with
  @nonnull + @SiZe(min = 1) in kotlin-springboot-reactive sample spec
  and regenerate the sample
- Clarify --inject-model-vendor-extensions/--inject-operation-vendor-extensions
  CLI help text to show quoted examples for multi-word annotation values

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ons CLI help

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…I help

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…vendor-extensions help

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Parse each --inject-model-vendor-extensions / --inject-operation-vendor-extensions
  occurrence as a single key=value pair (split only on the first '='), instead of
  running it through comma-based multi-tuple splitting. Annotation values containing
  commas (e.g. @SiZe(min = 1, max = 100)) are now taken literally and no longer get
  mis-parsed into bogus injection targets or require lossy quoting workarounds.
- Repeating the same key across multiple occurrences of either option now appends the
  values together (space-separated, in call order) instead of the later occurrence
  silently overwriting the earlier one, so multiple annotations can be layered onto
  the same target one occurrence at a time.
- Update CLI help text and Maven/Gradle plugin READMEs to document both behaviors.
- Add CodegenConfiguratorUtilsTest and GeneratorSettingsTest covering literal commas,
  multiple distinct targets, and same-key append semantics.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…String

Convert injectModelVendorExtensions/injectOperationVendorExtensions from
Map<String, String> to Map<String, List<String>> across CodegenConfig,
DefaultCodegen, GeneratorSettings, CodegenConfigurator, the CLI, and the
Gradle plugin DSL. Repeated CLI/Maven occurrences of the same key now
append as a new list element (instead of silently overwriting or being
space-joined), and the Gradle DSL properties become native
MapProperty<String, List<String>>. Mustache templates and
DefaultCodegen's normalization helpers already iterate vendor extensions
as lists, so no template changes are required.

Fix the two Java-side consumers that read an injected vendor extension
value as a scalar String and would otherwise break under the new list
type:
- AbstractCSharpCodegen's x-setter-visibility handling
- DefaultCodegen#fromOperation's x-codegen-request-body-name handling

Both now unwrap via DefaultCodegen.getObjectAsStringList(...) and use
the first element, since these extensions are scalar by nature.

Update CLI help text, Maven/Gradle plugin READMEs, and tests
(DefaultCodegenTest, GeneratorSettingsTest, SpringCodegenTest,
ParameterWiringRegressionTest) accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

2 issues found across 21 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java">

<violation number="1" location="modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java:61">
P1: When a YAML or JSON config uses the existing scalar form for an injected extension, `GeneratorSettings` now fails deserialization because it requires `List<String>` values. Preserve scalar compatibility by accepting a string-or-list value during config binding, or update the binding with single-value-to-array normalization.</violation>
</file>

<file name="modules/openapi-generator/src/test/resources/3_0/kotlin/petstore-with-extra-annotation.yaml">

<violation number="1" location="modules/openapi-generator/src/test/resources/3_0/kotlin/petstore-with-extra-annotation.yaml:99">
P2: The spec swap of @Nullable for @Size(min = 1) was applied, but the non-reactive sample was not regenerated: samples/server/petstore/kotlin-springboot-delegate/src/main/kotlin/org/openapitools/api/PetApi.kt:109 still renders `@NonNull @Nullable @NotNull` and keeps `import org.springframework.lang.Nullable`, while the reactive sample already emits `@Size(min = 1)`. This leaves the delegate sample stale and out of sync with the spec (and would fail the samples-up-to-date check). Regenerate samples/server/petstore/kotlin-springboot-delegate after changing the spec.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

private final Map<String, String> enumNameMappings;
private final Map<String, String> operationIdNameMappings;
private final Map<String, String> injectModelVendorExtensions;
private final Map<String, List<String>> injectModelVendorExtensions;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: When a YAML or JSON config uses the existing scalar form for an injected extension, GeneratorSettings now fails deserialization because it requires List<String> values. Preserve scalar compatibility by accepting a string-or-list value during config binding, or update the binding with single-value-to-array normalization.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java, line 61:

<comment>When a YAML or JSON config uses the existing scalar form for an injected extension, `GeneratorSettings` now fails deserialization because it requires `List<String>` values. Preserve scalar compatibility by accepting a string-or-list value during config binding, or update the binding with single-value-to-array normalization.</comment>

<file context>
@@ -58,8 +58,8 @@ public final class GeneratorSettings implements Serializable {
     private final Map<String, String> operationIdNameMappings;
-    private final Map<String, String> injectModelVendorExtensions;
-    private final Map<String, String> injectOperationVendorExtensions;
+    private final Map<String, List<String>> injectModelVendorExtensions;
+    private final Map<String, List<String>> injectOperationVendorExtensions;
     private final Map<String, String> openapiNormalizer;
</file context>

- jakarta.validation.constraints.Size
x-field-extra-annotation:
- "@NonNull"
- "@Size(min = 1)"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: The spec swap of @nullable for @SiZe(min = 1) was applied, but the non-reactive sample was not regenerated: samples/server/petstore/kotlin-springboot-delegate/src/main/kotlin/org/openapitools/api/PetApi.kt:109 still renders @NonNull @Nullable @NotNull and keeps import org.springframework.lang.Nullable, while the reactive sample already emits @Size(min = 1). This leaves the delegate sample stale and out of sync with the spec (and would fail the samples-up-to-date check). Regenerate samples/server/petstore/kotlin-springboot-delegate after changing the spec.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/test/resources/3_0/kotlin/petstore-with-extra-annotation.yaml, line 99:

<comment>The spec swap of @Nullable for @Size(min = 1) was applied, but the non-reactive sample was not regenerated: samples/server/petstore/kotlin-springboot-delegate/src/main/kotlin/org/openapitools/api/PetApi.kt:109 still renders `@NonNull @Nullable @NotNull` and keeps `import org.springframework.lang.Nullable`, while the reactive sample already emits `@Size(min = 1)`. This leaves the delegate sample stale and out of sync with the spec (and would fail the samples-up-to-date check). Regenerate samples/server/petstore/kotlin-springboot-delegate after changing the spec.</comment>

<file context>
@@ -93,10 +93,10 @@ paths:
           x-field-extra-annotation:
             - "@NonNull"
-            - "@Nullable"
+            - "@Size(min = 1)"
           schema:
             type: array
</file context>

…or injected vendor extensions

CLI help text: replace the --inject-model-vendor-extensions/
--inject-operation-vendor-extensions quoting example with
@foo(\"some string with spaces\") so it demonstrates both why the whole
key=value pair must be quoted (embedded space) and how to escape an
embedded double quote, in a single realistic example.

Config-file deserialization: injectModelVendorExtensions/
injectOperationVendorExtensions moved from Map<String, String> to
Map<String, List<String>>, but existing config files (e.g.
bin/configs/csharp-generichost-net10.yaml, already present on master)
author each value as a plain scalar string
(x-setter-visibility: private). Since GeneratorSettings lives in the
openapi-generator-core module (no Jackson dependency), the config-file
mapper binds these properties directly to GeneratorSettings.Builder's
private fields rather than through their withXxx() setters, which made
mixin-based fixes ineffective. Add a Jackson BeanDeserializerModifier
(VendorExtensionsMapDeserializerModifier) that replaces the deserializer
for any Map<String, List<String>> type with VendorExtensionsMapDeserializer,
which accepts either a scalar string or a list of strings per key and
always produces a List<String>. Registered via VendorExtensionsCompatModule
on the config-file ObjectMapper in CodegenConfigurator, independent of
whichever binding path (field vs. builder method) Jackson chooses.

Verified end-to-end: batch-generating bin/configs/csharp-generichost-net10.yaml
now succeeds again and produces identical C# output (private/internal/public
setter visibility and accessor overrides) as before this change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 5 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

- Fix jakarta/javax.validation.constraints.Size import conflict in the
  Kotlin petstore-with-extra-annotation.yaml fixture (samples default to
  Boot 2.7/javax); regenerate kotlin-springboot-reactive (removes the
  duplicate conflicting import) and kotlin-springboot-delegate (was
  never regenerated after the spec change).
- GeneratorSettings: deep-copy List<String> values in the Builder's
  bulk setters, the copy-constructor (newBuilder(copy)) path, and final
  construction, so injectModelVendorExtensions/injectOperationVendorExtensions
  lists are never shared/mutated across GeneratorSettings instances or
  builder copies.
- DefaultCodegen.fromOperation: thread the parameter-level injection
  match id across the fromParameter virtual-dispatch boundary via a
  short-lived instance field instead of calling the private 3-arg
  overload directly, so generators overriding the public
  fromParameter(Parameter, Set<String>) (Dart, TypeScript Fetch, Scala
  Akka HTTP, Java Helidon, C++ Boost Beast) run correctly again.
- CodegenConfigurator: stop setInjectModelVendorExtensions/
  setInjectOperationVendorExtensions from sharing the same map
  reference with generatorSettingsBuilder, which caused a subsequent
  addInject*VendorExtension call to append its value twice.
- Replace the custom VendorExtensionsCompatModule/
  VendorExtensionsMapDeserializer/VendorExtensionsMapDeserializerModifier
  with Jackson's built-in DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY,
  which fixes the same scalar-config-value compatibility case without
  matching every Map<String, List<String>> config property by shape or
  silently producing an empty map for non-object nodes.

Adds regression tests for the mutability/aliasing, double-append, and
virtual-dispatch fixes; re-verified the csharp-generichost-net10.yaml
config-file scalar-value repro end-to-end.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 14 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java">

<violation number="1" location="modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java:92">
P1: When a config file contains an injected key and the CLI adds another value for that key, generation throws `UnsupportedOperationException`. Deep-copy the lists when `CodegenConfigurator.fromFile` imports both extension maps, or copy an existing list before `addInject*VendorExtension` appends to it.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

}
Map<String, List<String>> copy = new HashMap<>();
source.forEach((key, value) ->
copy.put(key, Collections.unmodifiableList(new ArrayList<>(value))));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: When a config file contains an injected key and the CLI adds another value for that key, generation throws UnsupportedOperationException. Deep-copy the lists when CodegenConfigurator.fromFile imports both extension maps, or copy an existing list before addInject*VendorExtension appends to it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java, line 92:

<comment>When a config file contains an injected key and the CLI adds another value for that key, generation throws `UnsupportedOperationException`. Deep-copy the lists when `CodegenConfigurator.fromFile` imports both extension maps, or copy an existing list before `addInject*VendorExtension` appends to it.</comment>

<file context>
@@ -72,6 +72,45 @@ public final class GeneratorSettings implements Serializable {
+        }
+        Map<String, List<String>> copy = new HashMap<>();
+        source.forEach((key, value) ->
+                copy.put(key, Collections.unmodifiableList(new ArrayList<>(value))));
+        return Collections.unmodifiableMap(copy);
+    }
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hey @cubic-dev-ai, please re-review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Picazsoo I have started the AI code review. It will take a few minutes to complete.

Picazsoo and others added 2 commits September 22, 2026 00:11
…ant copy, thread safety, test coverage

- CodegenConfigurator.fromFile: deep-copy injectModelVendorExtensions/injectOperationVendorExtensions
  list values when importing from parsed GeneratorSettings, instead of sharing unmodifiable list
  references via putAll. Fixes UnsupportedOperationException when a config-file-defined key is
  later appended to via addInjectModelVendorExtension/addInjectOperationVendorExtension.
- CodegenConfigurator: remove redundant deepCopyInjectVendorExtensions helper; setInjectModel/
  OperationVendorExtensions now pass the map directly to the builder, which already deep-copies.
- DefaultCodegen: convert currentOperationVendorExtensionMatchOperationId from a plain instance
  field to a ThreadLocal<String> for thread safety.
- DefaultCodegenTest: extend testFromOperationPreservesFromParameterVirtualDispatch to also assert
  parameter-level vendor-extension injection works through an overriding fromParameter.
- CodegenConfiguratorTest: add regression test for the fromFile deep-copy fix.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The kotlin-spring apiInterface.mustache template unconditionally imports
javax.validation.constraints.Size as part of its standard bean-validation
import block, so injecting the same import via x-extra-imports caused a
'Conflicting import, imported name Size is ambiguous' Kotlin compile error.

Replace the redundant Size import with NotEmpty (not part of the template's
unconditional import block) and add @notempty to the field-extra-annotation
list, preserving the multi-value list demonstration. Regenerated the
kotlin-springboot-delegate and kotlin-springboot-reactive samples.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

3 issues found across 60 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java:4287">
P2: Both new specs tag every operation `employee`, yet the tests assert generated files named `OrgsApi.java` (inject spec, paths `/orgs/...`) and `EmployeesApi.java` (request-body spec, paths `/employees/...`). openapi-generator derives the API class name from the tag via `toApiName(tag)`, so a tag of `employee` yields `EmployeeApi.java` — see also `testHasOperationParameterExtraAnnotation_issue18224` just above, which asserts the tag-derived `TestApi.java`. If the generated file is in fact `EmployeeApi.java`, `files.get("OrgsApi.java")` / `files.get("EmployeesApi.java")` return null and `JavaFileAssert.assertThat(null)` throws, failing both new tests. Please run these two tests and correct the asserted filenames (or the spec tags) to match the actual output.</violation>
</file>

<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java:81">
P1: A single injected value is now stored as a list for every vendor extension, so generic scalar extensions such as `x-cpp-type` can fail with `ClassCastException`. Preserve scalar values for scalar extensions and use lists only where the consumer supports them.</violation>
</file>

<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfiguratorUtils.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfiguratorUtils.java:208">
P2: When existing CLI or Maven configuration supplies multiple model injection targets in one option occurrence, this change silently stops injecting every target after the first. Preserve the established comma-separated model-pair behavior, or introduce an unambiguous compatibility parser while keeping single-pair parsing for operation values that contain commas.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

private Map<String, String> enumNameMappings = new HashMap<>();
private Map<String, String> operationIdNameMappings = new HashMap<>();
private Map<String, String> injectModelVendorExtensions = new HashMap<>();
private Map<String, List<String>> injectModelVendorExtensions = new HashMap<>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: A single injected value is now stored as a list for every vendor extension, so generic scalar extensions such as x-cpp-type can fail with ClassCastException. Preserve scalar values for scalar extensions and use lists only where the consumer supports them.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java, line 81:

<comment>A single injected value is now stored as a list for every vendor extension, so generic scalar extensions such as `x-cpp-type` can fail with `ClassCastException`. Preserve scalar values for scalar extensions and use lists only where the consumer supports them.</comment>

<file context>
@@ -74,9 +75,10 @@ public class CodegenConfigurator {
     private Map<String, String> enumNameMappings = new HashMap<>();
     private Map<String, String> operationIdNameMappings = new HashMap<>();
-    private Map<String, String> injectModelVendorExtensions = new HashMap<>();
+    private Map<String, List<String>> injectModelVendorExtensions = new HashMap<>();
     private Map<String, String> openapiNormalizer = new HashMap<>();
     private Set<String> languageSpecificPrimitives = new HashSet<>();
</file context>

Map<String, File> files = generator.opts(input).generate().stream()
.collect(Collectors.toMap(File::getName, Function.identity()));

JavaFileAssert.assertThat(files.get("OrgsApi.java"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Both new specs tag every operation employee, yet the tests assert generated files named OrgsApi.java (inject spec, paths /orgs/...) and EmployeesApi.java (request-body spec, paths /employees/...). openapi-generator derives the API class name from the tag via toApiName(tag), so a tag of employee yields EmployeeApi.java — see also testHasOperationParameterExtraAnnotation_issue18224 just above, which asserts the tag-derived TestApi.java. If the generated file is in fact EmployeeApi.java, files.get("OrgsApi.java") / files.get("EmployeesApi.java") return null and JavaFileAssert.assertThat(null) throws, failing both new tests. Please run these two tests and correct the asserted filenames (or the spec tags) to match the actual output.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java, line 4287:

<comment>Both new specs tag every operation `employee`, yet the tests assert generated files named `OrgsApi.java` (inject spec, paths `/orgs/...`) and `EmployeesApi.java` (request-body spec, paths `/employees/...`). openapi-generator derives the API class name from the tag via `toApiName(tag)`, so a tag of `employee` yields `EmployeeApi.java` — see also `testHasOperationParameterExtraAnnotation_issue18224` just above, which asserts the tag-derived `TestApi.java`. If the generated file is in fact `EmployeeApi.java`, `files.get("OrgsApi.java")` / `files.get("EmployeesApi.java")` return null and `JavaFileAssert.assertThat(null)` throws, failing both new tests. Please run these two tests and correct the asserted filenames (or the spec tags) to match the actual output.</comment>

<file context>
@@ -4184,7 +4184,126 @@ public void testHasOperationParameterExtraAnnotation_issue18224() throws IOExcep
+        Map<String, File> files = generator.opts(input).generate().stream()
+                .collect(Collectors.toMap(File::getName, Function.identity()));
+
+        JavaFileAssert.assertThat(files.get("OrgsApi.java"))
+                .assertMethod("createEmployee")
+                .assertParameter("employee")
</file context>

final Map<String, String> map = createMapFromKeyValuePairs(injectModelVendorExtensions);
for (Map.Entry<String, String> entry : map.entrySet()) {
configurator.addInjectModelVendorExtension(entry.getKey().trim(), entry.getValue().trim());
final Pair<String, String> pair = parseSingleInjectVendorExtensionKvp(injectModelVendorExtensions);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: When existing CLI or Maven configuration supplies multiple model injection targets in one option occurrence, this change silently stops injecting every target after the first. Preserve the established comma-separated model-pair behavior, or introduce an unambiguous compatibility parser while keeping single-pair parsing for operation values that contain commas.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfiguratorUtils.java, line 208:

<comment>When existing CLI or Maven configuration supplies multiple model injection targets in one option occurrence, this change silently stops injecting every target after the first. Preserve the established comma-separated model-pair behavior, or introduce an unambiguous compatibility parser while keeping single-pair parsing for operation values that contain commas.</comment>

<file context>
@@ -205,10 +205,48 @@ public static void applyInjectModelVendorExtensionsKvpList(List<String> injectMo
-        final Map<String, String> map = createMapFromKeyValuePairs(injectModelVendorExtensions);
-        for (Map.Entry<String, String> entry : map.entrySet()) {
-            configurator.addInjectModelVendorExtension(entry.getKey().trim(), entry.getValue().trim());
+        final Pair<String, String> pair = parseSingleInjectVendorExtensionKvp(injectModelVendorExtensions);
+        if (pair != null) {
+            configurator.addInjectModelVendorExtension(pair.getLeft().trim(), pair.getRight().trim());
</file context>

@Picazsoo
Picazsoo marked this pull request as draft September 21, 2026 22:58
…n and tighten test assertions

- DefaultCodegen: getInjectedVendorExtensionParts now splits the remainder at the last '.x-'
  boundary instead of the first dot, so a parameter or property base name containing a dot
  (e.g. 'org.id') is no longer mis-split. The model-level injection loop in
  postProcessAllModels now reuses this same helper instead of a naive split(".", 3), fixing
  the identical class of bug for dotted property base names.
- DefaultCodegenTest: added regression tests for both the operation/parameter-level and
  model/property-level dotted-name fixes.
- Removed the contradictory @nonnull annotation from the optional 'file' property in the
  Kotlin extra-annotation fixture (uploadFile's file part is not required, so @nonnull
  conflicted with its nullable Part?/MultipartFile type); regenerated kotlin-springboot-delegate
  and kotlin-springboot-reactive samples.
- ParameterWiringRegressionTest (gradle plugin): tightened the injectOperationVendorExtensions
  wiring test to assert the injected '@deprecated' annotation appears exactly once, so the test
  can catch a regression where injection stops being operation-specific.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Picazsoo

Copy link
Copy Markdown
Contributor Author

Hi @wing328 - I think this one should wait for 7.27.0. I will not have enough time to properly address all the issues.

@wing328 wing328 removed this from the 7.26.0 milestone Sep 27, 2026

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants