Skip to content

[Spring] Add JsonProperty access=READ_ONLY for readOnly properties - #24929

Open
antoineclech-arkea wants to merge 2 commits into
OpenAPITools:masterfrom
antoineclech-arkea:fix/readonly-json-property-access
Open

antoineclech-arkea wants to merge 2 commits into
OpenAPITools:masterfrom
antoineclech-arkea:fix/readonly-json-property-access

Conversation

@antoineclech-arkea

@antoineclech-arkea antoineclech-arkea commented Sep 11, 2026

Copy link
Copy Markdown

Fixes #20612

Fixes generation of read-only properties in Spring models to include @JsonProperty(access = JsonProperty.Access.READ_ONLY) on setters.

This prevents Jackson from accepting read-only properties during deserialization, enforcing the OpenAPI contract.

Problem

Issue #20612: The OpenAPI Generator doesn't properly propagate readOnly property information. Generated setters for readOnly properties are treated as fully writable by Jackson,
violating the OpenAPI 3.0.0 specification.

Before this fix:

@JsonProperty("id")
public void setId(String id) { ... }  // ❌ Jackson accepts this value

After this fix:
@JsonProperty(value = "id", access = JsonProperty.Access.READ_ONLY)
public void setId(String id) { ... }  // ✅ Jackson rejects this value

Solution

Modified the Mustache template for Spring POJO generation to emit the access = JsonProperty.Access.READ_ONLY parameter when a property is marked as readOnly.

File changed: modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache (line 259)

Scope

- Generator: Spring only (clean, focused PR)
- Other Java generators: Can follow with similar changes in separate PRs
- Breaking changes: None - fully backward compatible

PR checklist


- [x] Read the contribution guidelines.
- [ ] Run the following to build the project and update samples:
./mvnw clean package || exit
./bin/generate-samples.sh ./bin/configs/java* || exit
./bin/utils/export_docs_generators.sh || exit
- (Note: Ready to run if maintainers request sample verification)
- [x] PR targets Spring generator - @mention technical committee members for review

@antoineclech-arkea
antoineclech-arkea marked this pull request as ready for review September 11, 2026 09:43

@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 1 file

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/main/resources/JavaSpring/pojo.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache:259">
P1: When Lombok generates the setter through `lombok.Setter` or `lombok.Data`, this line is not rendered and no Lombok-side annotation sets `READ_ONLY`, so read-only properties remain writable by Jackson. Apply the access setting to the Lombok-generated accessor or field path as well.</violation>

<violation number="2" location="modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache:259">
P1: When `openApiNullable` is enabled for a non-required nullable read-only property, this guard suppresses the new annotation, so Jackson still auto-detects the setter and accepts request values. Emit `READ_ONLY` for this case too while retaining any nullable-specific mapping.</violation>

<violation number="3" location="modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache:259">
P2: When the Spring generator runs with `withXml` enabled, this change silently removes `@JacksonXmlProperty` and `@JacksonXmlElementWrapper` from every generated setter. The original setter used the shared `{{>jackson_annotations}}` partial, which emits those XML annotations under `{{#withXml}}`; the new inline `@JsonProperty(...)` only emits the JSON annotation, so XML deserialization mapping (element name/wrapper) on setters is lost while the getter keeps them. Preserve the partial and add the `access = JsonProperty.Access.READ_ONLY` parameter to `jackson_annotations.mustache` instead of replacing the partial inline.</violation>
</file>

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

Re-trigger cubic

@Deprecated
{{/deprecated}}
{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{>jackson_annotations}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}} public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {
{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}} @JsonProperty(value = "{{baseName}}"{{#isReadOnly}}, access = JsonProperty.Access.READ_ONLY{{/isReadOnly}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}} public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {

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 Lombok generates the setter through lombok.Setter or lombok.Data, this line is not rendered and no Lombok-side annotation sets READ_ONLY, so read-only properties remain writable by Jackson. Apply the access setting to the Lombok-generated accessor or field path as well.

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/resources/JavaSpring/pojo.mustache, line 259:

<comment>When Lombok generates the setter through `lombok.Setter` or `lombok.Data`, this line is not rendered and no Lombok-side annotation sets `READ_ONLY`, so read-only properties remain writable by Jackson. Apply the access setting to the Lombok-generated accessor or field path as well.</comment>

<file context>
@@ -256,7 +256,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
   @Deprecated
   {{/deprecated}}
-{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{>jackson_annotations}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}}  public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {
+{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}}  @JsonProperty(value = "{{baseName}}"{{#isReadOnly}}, access = JsonProperty.Access.READ_ONLY{{/isReadOnly}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}}  public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {
     this.{{name}} = {{name}};
   }
</file context>

@Deprecated
{{/deprecated}}
{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{>jackson_annotations}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}} public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {
{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}} @JsonProperty(value = "{{baseName}}"{{#isReadOnly}}, access = JsonProperty.Access.READ_ONLY{{/isReadOnly}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}} public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {

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 openApiNullable is enabled for a non-required nullable read-only property, this guard suppresses the new annotation, so Jackson still auto-detects the setter and accepts request values. Emit READ_ONLY for this case too while retaining any nullable-specific mapping.

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/resources/JavaSpring/pojo.mustache, line 259:

<comment>When `openApiNullable` is enabled for a non-required nullable read-only property, this guard suppresses the new annotation, so Jackson still auto-detects the setter and accepts request values. Emit `READ_ONLY` for this case too while retaining any nullable-specific mapping.</comment>

<file context>
@@ -256,7 +256,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
   @Deprecated
   {{/deprecated}}
-{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{>jackson_annotations}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}}  public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {
+{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}}  @JsonProperty(value = "{{baseName}}"{{#isReadOnly}}, access = JsonProperty.Access.READ_ONLY{{/isReadOnly}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}}  public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {
     this.{{name}} = {{name}};
   }
</file context>

@Deprecated
{{/deprecated}}
{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{>jackson_annotations}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}} public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {
{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}} @JsonProperty(value = "{{baseName}}"{{#isReadOnly}}, access = JsonProperty.Access.READ_ONLY{{/isReadOnly}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}} public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {

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 the Spring generator runs with withXml enabled, this change silently removes @JacksonXmlProperty and @JacksonXmlElementWrapper from every generated setter. The original setter used the shared {{>jackson_annotations}} partial, which emits those XML annotations under {{#withXml}}; the new inline @JsonProperty(...) only emits the JSON annotation, so XML deserialization mapping (element name/wrapper) on setters is lost while the getter keeps them. Preserve the partial and add the access = JsonProperty.Access.READ_ONLY parameter to jackson_annotations.mustache instead of replacing the partial inline.

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/resources/JavaSpring/pojo.mustache, line 259:

<comment>When the Spring generator runs with `withXml` enabled, this change silently removes `@JacksonXmlProperty` and `@JacksonXmlElementWrapper` from every generated setter. The original setter used the shared `{{>jackson_annotations}}` partial, which emits those XML annotations under `{{#withXml}}`; the new inline `@JsonProperty(...)` only emits the JSON annotation, so XML deserialization mapping (element name/wrapper) on setters is lost while the getter keeps them. Preserve the partial and add the `access = JsonProperty.Access.READ_ONLY` parameter to `jackson_annotations.mustache` instead of replacing the partial inline.</comment>

<file context>
@@ -256,7 +256,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
   @Deprecated
   {{/deprecated}}
-{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{>jackson_annotations}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}}  public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {
+{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}}  @JsonProperty(value = "{{baseName}}"{{#isReadOnly}}, access = JsonProperty.Access.READ_ONLY{{/isReadOnly}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}}  public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {
     this.{{name}} = {{name}};
   }
</file context>

@antoineclech-arkea
antoineclech-arkea force-pushed the fix/readonly-json-property-access branch from bdbded2 to c7e18e0 Compare September 11, 2026 09:57
@antoineclech-arkea

Copy link
Copy Markdown
Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

@cubic review

@antoineclech-arkea 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 3 files

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

Re-trigger cubic

@antoineclech-arkea
antoineclech-arkea force-pushed the fix/readonly-json-property-access branch from c7e18e0 to f9ecd99 Compare September 11, 2026 12:44
@antoineclech-arkea

Copy link
Copy Markdown
Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

@cubic review

@antoineclech-arkea 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.

1 existing issue remains and 1 new issue found across 3 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/main/resources/JavaSpring/jackson_annotations.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/JavaSpring/jackson_annotations.mustache:1">
P2: This template change alters generated output for every readOnly property in existing Spring samples, but no samples were regenerated. The committed samples (e.g. samples/client/petstore/spring-http-interface/src/main/java/org/openapitools/model/HasOnlyReadOnlyDto.java, which currently emits `@JsonProperty("bar")` for its readOnly fields) will now produce `@JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY)`, so the sample-verification CI will fail. Regenerate the affected Spring samples (./bin/generate-samples.sh for the spring configs) as part of this PR.</violation>
</file>

Requires human review: Auto-approval blocked because this review re-detected 1 unresolved issue already reported by Cubic.

Re-trigger cubic

@@ -1,4 +1,4 @@
@JsonProperty("{{baseName}}")
@JsonProperty({{#isReadOnly}}value = {{/isReadOnly}}"{{baseName}}"{{#isReadOnly}}, access = JsonProperty.Access.READ_ONLY{{/isReadOnly}})

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: This template change alters generated output for every readOnly property in existing Spring samples, but no samples were regenerated. The committed samples (e.g. samples/client/petstore/spring-http-interface/src/main/java/org/openapitools/model/HasOnlyReadOnlyDto.java, which currently emits @JsonProperty("bar") for its readOnly fields) will now produce @JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY), so the sample-verification CI will fail. Regenerate the affected Spring samples (./bin/generate-samples.sh for the spring configs) as part of this PR.

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/resources/JavaSpring/jackson_annotations.mustache, line 1:

<comment>This template change alters generated output for every readOnly property in existing Spring samples, but no samples were regenerated. The committed samples (e.g. samples/client/petstore/spring-http-interface/src/main/java/org/openapitools/model/HasOnlyReadOnlyDto.java, which currently emits `@JsonProperty("bar")` for its readOnly fields) will now produce `@JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY)`, so the sample-verification CI will fail. Regenerate the affected Spring samples (./bin/generate-samples.sh for the spring configs) as part of this PR.</comment>

<file context>
@@ -1,4 +1,4 @@
-  @JsonProperty("{{baseName}}")
+  @JsonProperty({{#isReadOnly}}value = {{/isReadOnly}}"{{baseName}}"{{#isReadOnly}}, access = JsonProperty.Access.READ_ONLY{{/isReadOnly}})
 {{#withXml}}
   @JacksonXmlProperty(localName = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#isXmlAttribute}}, isAttribute = true{{/isXmlAttribute}}{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
</file context>

@antoineclech-arkea
antoineclech-arkea force-pushed the fix/readonly-json-property-access branch from f9ecd99 to 546d04d Compare September 11, 2026 13:00
@antoineclech-arkea

Copy link
Copy Markdown
Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

@cubic review

@antoineclech-arkea 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.

No issues found across 6 files

Re-trigger cubic

Fixes OpenAPITools#20612

Fixes generation of read-only properties in Spring models to include
@JsonProperty(access = JsonProperty.Access.READ_ONLY) on setters.

This prevents Jackson from accepting read-only properties during
deserialization, enforcing the OpenAPI contract.

Changes:
- Modified jackson_annotations.mustache to emit access=READ_ONLY for readOnly properties
- Added unit test with readOnly property test spec
- Regenerated affected Spring samples
@antoineclech-arkea
antoineclech-arkea force-pushed the fix/readonly-json-property-access branch from 66b33e4 to f8f08c1 Compare September 11, 2026 13:22
@antoineclech-arkea

Copy link
Copy Markdown
Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

@cubic review

@antoineclech-arkea 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.

2 issues found across 15 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="samples/client/petstore/spring-http-interface-noResponseEntity/src/main/java/org/openapitools/model/HasOnlyReadOnlyDto.java">

<violation number="1" location="samples/client/petstore/spring-http-interface-noResponseEntity/src/main/java/org/openapitools/model/HasOnlyReadOnlyDto.java:42">
P2: Jackson `READ_ONLY` means serialize but never deserialize. That is the right contract for a server model (don't accept readOnly values in requests), but this shared template is also used by client-side Spring generators — this very sample is `samples/client/petstore/spring-http-interface-noResponseEntity`. For a client, a readOnly value arrives in the server response and must be deserialized; with `READ_ONLY` on the getter/setter Jackson now silently skips it, so `getBar()`/`getFoo()` return null for values the server actually sent. This is a real behavior change for Spring clients that the PR does not account for (it only frames the server benefit). Confirm client deserialization of readOnly values is not needed, or gate the `READ_ONLY` emission to server-side generation.</violation>
</file>

<file name="samples/client/petstore/spring-http-interface/src/main/java/org/openapitools/model/HasOnlyReadOnlyDto.java">

<violation number="1" location="samples/client/petstore/spring-http-interface/src/main/java/org/openapitools/model/HasOnlyReadOnlyDto.java:42">
P2: For these Spring CLIENT models the shared READ_ONLY setter change prevents Jackson from deserializing readOnly fields, so the client silently receives null for server-generated values (e.g. IDs) in API responses. OpenAPI readOnly means 'may be sent in responses but should not be sent in requests', so a client must still be able to read these fields. Gate the READ_ONLY emission to server-side generators only, or apply it only where the model is used for request deserialization.</violation>
</file>

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

Re-trigger cubic

*/

@JsonProperty("bar")
@JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY)

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: Jackson READ_ONLY means serialize but never deserialize. That is the right contract for a server model (don't accept readOnly values in requests), but this shared template is also used by client-side Spring generators — this very sample is samples/client/petstore/spring-http-interface-noResponseEntity. For a client, a readOnly value arrives in the server response and must be deserialized; with READ_ONLY on the getter/setter Jackson now silently skips it, so getBar()/getFoo() return null for values the server actually sent. This is a real behavior change for Spring clients that the PR does not account for (it only frames the server benefit). Confirm client deserialization of readOnly values is not needed, or gate the READ_ONLY emission to server-side generation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/spring-http-interface-noResponseEntity/src/main/java/org/openapitools/model/HasOnlyReadOnlyDto.java, line 42:

<comment>Jackson `READ_ONLY` means serialize but never deserialize. That is the right contract for a server model (don't accept readOnly values in requests), but this shared template is also used by client-side Spring generators — this very sample is `samples/client/petstore/spring-http-interface-noResponseEntity`. For a client, a readOnly value arrives in the server response and must be deserialized; with `READ_ONLY` on the getter/setter Jackson now silently skips it, so `getBar()`/`getFoo()` return null for values the server actually sent. This is a real behavior change for Spring clients that the PR does not account for (it only frames the server benefit). Confirm client deserialization of readOnly values is not needed, or gate the `READ_ONLY` emission to server-side generation.</comment>

<file context>
@@ -39,12 +39,12 @@ public HasOnlyReadOnlyDto bar(@Nullable String bar) {
    */
   
-  @JsonProperty("bar")
+  @JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY)
   public @Nullable String getBar() {
     return bar;
</file context>

*/

@JsonProperty("bar")
@JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY)

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: For these Spring CLIENT models the shared READ_ONLY setter change prevents Jackson from deserializing readOnly fields, so the client silently receives null for server-generated values (e.g. IDs) in API responses. OpenAPI readOnly means 'may be sent in responses but should not be sent in requests', so a client must still be able to read these fields. Gate the READ_ONLY emission to server-side generators only, or apply it only where the model is used for request deserialization.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/spring-http-interface/src/main/java/org/openapitools/model/HasOnlyReadOnlyDto.java, line 42:

<comment>For these Spring CLIENT models the shared READ_ONLY setter change prevents Jackson from deserializing readOnly fields, so the client silently receives null for server-generated values (e.g. IDs) in API responses. OpenAPI readOnly means 'may be sent in responses but should not be sent in requests', so a client must still be able to read these fields. Gate the READ_ONLY emission to server-side generators only, or apply it only where the model is used for request deserialization.</comment>

<file context>
@@ -39,12 +39,12 @@ public HasOnlyReadOnlyDto bar(@Nullable String bar) {
    */
   
-  @JsonProperty("bar")
+  @JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY)
   public @Nullable String getBar() {
     return bar;
</file context>

@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.

6 issues found across 78 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="samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/model/HasOnlyReadOnly.java">

<violation number="1" location="samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/model/HasOnlyReadOnly.java:45">
P2: This is a silent runtime behavior change for every existing Spring-generated API, not the "fully backward compatible" change the PR claims. Once @JsonProperty(access = READ_ONLY) is on a setter, Jackson stops calling it during deserialization, so any client that currently sends readOnly fields (e.g. an id reference) will have those values silently dropped rather than stored — the JSON is ignored, not rejected with an error. Existing generated clients/servers built on the previous behavior will diverge without any exception being thrown. Confirm this compatibility impact is intended and update the PR's "no breaking changes" claim, or restrict the enforcement to new opt-in generators.</violation>
</file>

<file name="samples/server/petstore/springboot-x-implements-skip/src/main/java/org/openapitools/model/ReadOnlyFirstDto.java">

<violation number="1" location="samples/server/petstore/springboot-x-implements-skip/src/main/java/org/openapitools/model/ReadOnlyFirstDto.java:45">
P2: The READ_ONLY annotations are only emitted in the non-Lombok getter/setter blocks of JavaSpring/pojo.mustache (lines 242 and 259). SpringCodegen exposes a real lombokAnnotations option (Getter/Setter/Data); for those configs the getter/setter (and thus any @JsonProperty) are not generated, so readOnly properties carry no READ_ONLY access and Jackson still accepts them during deserialization. This contradicts the PR's claim that the change prevents Jackson from accepting read-only properties in Spring models, and means the fix is silently incomplete for Lombok-based Spring generation.</violation>
</file>

<file name="samples/server/petstore/springboot-include-http-request-context/src/main/java/org/openapitools/model/NameDto.java">

<violation number="1" location="samples/server/petstore/springboot-include-http-request-context/src/main/java/org/openapitools/model/NameDto.java:84">
P3: The `access = JsonProperty.Access.READ_ONLY` is emitted on both the getter and the setter. Jackson merges `@JsonProperty` access across all accessors of a logical property, so a single accessor annotation (getter or setter) is sufficient to make the property read-only; the duplicate on the second accessor is redundant and only adds noise to the generated public API. Emit it on one accessor per readOnly property.</violation>
</file>

<file name="samples/client/petstore/spring-http-interface-useHttpServiceProxyFactoryInterfacesConfigurator/src/main/java/org/openapitools/model/Name.java">

<violation number="1" location="samples/client/petstore/spring-http-interface-useHttpServiceProxyFactoryInterfacesConfigurator/src/main/java/org/openapitools/model/Name.java:79">
P2: `JsonProperty.Access.READ_ONLY` on the setter makes Jackson skip the property entirely during deserialization (READ_ONLY disables the setter, not just the fluent method). Because the Spring generator emits one shared model used for both request and response DTOs, this also stops readOnly fields from being populated whenever that model is deserialized on the client side — e.g. a Spring WebClient or a shared DTO parsing a server response. Server-generated readOnly fields such as `id` or `createdAt` will come back null after this change, which the PR's "no breaking changes" claim does not cover. Note the `Java` generator deliberately avoids this by making readOnly setters `private` with an empty-`JsonNullable` path rather than annotating READ_ONLY. Confirm this template is only used where the model is never deserialized from responses; otherwise the READ_ONLY scope needs to be applied so readOnly response fields are still readable.</violation>
</file>

<file name="samples/server/petstore/springboot-builtin-validation/src/main/java/org/openapitools/model/HasOnlyReadOnly.java">

<violation number="1" location="samples/server/petstore/springboot-builtin-validation/src/main/java/org/openapitools/model/HasOnlyReadOnly.java:45">
P3: The chain/fluent setters `bar(String)` and `foo(String)` are emitted with no Jackson annotation at all, while `access = READ_ONLY` is applied only to the getter and void setter. Whether a readOnly value is refused during request deserialization therefore depends entirely on Jackson merging the fluent setters into the same logical read-only property. Make the guarantee explicit by emitting `@JsonProperty(value = "...", access = JsonProperty.Access.READ_ONLY)` on the chain setters for readOnly vars in JavaSpring/pojo.mustache, so the enforcement is self-documenting and robust.</violation>
</file>

<file name="samples/server/petstore/springboot-spring-pageable-delegatePattern/src/main/java/org/openapitools/model/ReadOnlyFirst.java">

<violation number="1" location="samples/server/petstore/springboot-spring-pageable-delegatePattern/src/main/java/org/openapitools/model/ReadOnlyFirst.java:43">
P3: This fix adds READ_ONLY access for readOnly properties, but the Spring generator does not handle the symmetric writeOnly case. The existing JavaJaxRS generators already emit access = JsonProperty.Access.WRITE_ONLY for writeOnly properties, while Spring emits nothing, so writeOnly properties (e.g. passwords, secrets) remain fully serialized into responses. Extend the same conditional handling to writeOnly to match the established JaxRS pattern and avoid leaking write-only data.</violation>
</file>

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

Re-trigger cubic


@Schema(name = "bar", accessMode = Schema.AccessMode.READ_ONLY, requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@JsonProperty("bar")
@JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY)

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: This is a silent runtime behavior change for every existing Spring-generated API, not the "fully backward compatible" change the PR claims. Once @JsonProperty(access = READ_ONLY) is on a setter, Jackson stops calling it during deserialization, so any client that currently sends readOnly fields (e.g. an id reference) will have those values silently dropped rather than stored — the JSON is ignored, not rejected with an error. Existing generated clients/servers built on the previous behavior will diverge without any exception being thrown. Confirm this compatibility impact is intended and update the PR's "no breaking changes" claim, or restrict the enforcement to new opt-in generators.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/model/HasOnlyReadOnly.java, line 45:

<comment>This is a silent runtime behavior change for every existing Spring-generated API, not the "fully backward compatible" change the PR claims. Once @JsonProperty(access = READ_ONLY) is on a setter, Jackson stops calling it during deserialization, so any client that currently sends readOnly fields (e.g. an id reference) will have those values silently dropped rather than stored — the JSON is ignored, not rejected with an error. Existing generated clients/servers built on the previous behavior will diverge without any exception being thrown. Confirm this compatibility impact is intended and update the PR's "no breaking changes" claim, or restrict the enforcement to new opt-in generators.</comment>

<file context>
@@ -42,12 +42,12 @@ public HasOnlyReadOnly bar(@Nullable String bar) {
   
   @Schema(name = "bar", accessMode = Schema.AccessMode.READ_ONLY, requiredMode = Schema.RequiredMode.NOT_REQUIRED)
-  @JsonProperty("bar")
+  @JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY)
   public @Nullable String getBar() {
     return bar;
</file context>


@Schema(name = "bar", accessMode = Schema.AccessMode.READ_ONLY, requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@JsonProperty("bar")
@JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY)

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 READ_ONLY annotations are only emitted in the non-Lombok getter/setter blocks of JavaSpring/pojo.mustache (lines 242 and 259). SpringCodegen exposes a real lombokAnnotations option (Getter/Setter/Data); for those configs the getter/setter (and thus any @JsonProperty) are not generated, so readOnly properties carry no READ_ONLY access and Jackson still accepts them during deserialization. This contradicts the PR's claim that the change prevents Jackson from accepting read-only properties in Spring models, and means the fix is silently incomplete for Lombok-based Spring generation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/springboot-x-implements-skip/src/main/java/org/openapitools/model/ReadOnlyFirstDto.java, line 45:

<comment>The READ_ONLY annotations are only emitted in the non-Lombok getter/setter blocks of JavaSpring/pojo.mustache (lines 242 and 259). SpringCodegen exposes a real lombokAnnotations option (Getter/Setter/Data); for those configs the getter/setter (and thus any @JsonProperty) are not generated, so readOnly properties carry no READ_ONLY access and Jackson still accepts them during deserialization. This contradicts the PR's claim that the change prevents Jackson from accepting read-only properties in Spring models, and means the fix is silently incomplete for Lombok-based Spring generation.</comment>

<file context>
@@ -42,12 +42,12 @@ public ReadOnlyFirstDto bar(@Nullable String bar) {
   
   @Schema(name = "bar", accessMode = Schema.AccessMode.READ_ONLY, requiredMode = Schema.RequiredMode.NOT_REQUIRED)
-  @JsonProperty("bar")
+  @JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY)
   public @Nullable String getBar() {
     return bar;
</file context>

*/

@JsonProperty("snake_case")
@JsonProperty(value = "snake_case", access = JsonProperty.Access.READ_ONLY)

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: JsonProperty.Access.READ_ONLY on the setter makes Jackson skip the property entirely during deserialization (READ_ONLY disables the setter, not just the fluent method). Because the Spring generator emits one shared model used for both request and response DTOs, this also stops readOnly fields from being populated whenever that model is deserialized on the client side — e.g. a Spring WebClient or a shared DTO parsing a server response. Server-generated readOnly fields such as id or createdAt will come back null after this change, which the PR's "no breaking changes" claim does not cover. Note the Java generator deliberately avoids this by making readOnly setters private with an empty-JsonNullable path rather than annotating READ_ONLY. Confirm this template is only used where the model is never deserialized from responses; otherwise the READ_ONLY scope needs to be applied so readOnly response fields are still readable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/spring-http-interface-useHttpServiceProxyFactoryInterfacesConfigurator/src/main/java/org/openapitools/model/Name.java, line 79:

<comment>`JsonProperty.Access.READ_ONLY` on the setter makes Jackson skip the property entirely during deserialization (READ_ONLY disables the setter, not just the fluent method). Because the Spring generator emits one shared model used for both request and response DTOs, this also stops readOnly fields from being populated whenever that model is deserialized on the client side — e.g. a Spring WebClient or a shared DTO parsing a server response. Server-generated readOnly fields such as `id` or `createdAt` will come back null after this change, which the PR's "no breaking changes" claim does not cover. Note the `Java` generator deliberately avoids this by making readOnly setters `private` with an empty-`JsonNullable` path rather than annotating READ_ONLY. Confirm this template is only used where the model is never deserialized from responses; otherwise the READ_ONLY scope needs to be applied so readOnly response fields are still readable.</comment>

<file context>
@@ -76,12 +76,12 @@ public Name snakeCase(@Nullable Integer snakeCase) {
    */
   
-  @JsonProperty("snake_case")
+  @JsonProperty(value = "snake_case", access = JsonProperty.Access.READ_ONLY)
   public @Nullable Integer getSnakeCase() {
     return snakeCase;
</file context>


@Schema(name = "snake_case", accessMode = Schema.AccessMode.READ_ONLY, requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@JsonProperty("snake_case")
@JsonProperty(value = "snake_case", access = JsonProperty.Access.READ_ONLY)

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.

P3: The access = JsonProperty.Access.READ_ONLY is emitted on both the getter and the setter. Jackson merges @JsonProperty access across all accessors of a logical property, so a single accessor annotation (getter or setter) is sufficient to make the property read-only; the duplicate on the second accessor is redundant and only adds noise to the generated public API. Emit it on one accessor per readOnly property.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/springboot-include-http-request-context/src/main/java/org/openapitools/model/NameDto.java, line 84:

<comment>The `access = JsonProperty.Access.READ_ONLY` is emitted on both the getter and the setter. Jackson merges `@JsonProperty` access across all accessors of a logical property, so a single accessor annotation (getter or setter) is sufficient to make the property read-only; the duplicate on the second accessor is redundant and only adds noise to the generated public API. Emit it on one accessor per readOnly property.</comment>

<file context>
@@ -81,12 +81,12 @@ public NameDto snakeCase(@Nullable Integer snakeCase) {
   
   @Schema(name = "snake_case", accessMode = Schema.AccessMode.READ_ONLY, requiredMode = Schema.RequiredMode.NOT_REQUIRED)
-  @JsonProperty("snake_case")
+  @JsonProperty(value = "snake_case", access = JsonProperty.Access.READ_ONLY)
   public @Nullable Integer getSnakeCase() {
     return snakeCase;
</file context>


@Schema(name = "bar", accessMode = Schema.AccessMode.READ_ONLY, requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@JsonProperty("bar")
@JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY)

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.

P3: The chain/fluent setters bar(String) and foo(String) are emitted with no Jackson annotation at all, while access = READ_ONLY is applied only to the getter and void setter. Whether a readOnly value is refused during request deserialization therefore depends entirely on Jackson merging the fluent setters into the same logical read-only property. Make the guarantee explicit by emitting @JsonProperty(value = "...", access = JsonProperty.Access.READ_ONLY) on the chain setters for readOnly vars in JavaSpring/pojo.mustache, so the enforcement is self-documenting and robust.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/springboot-builtin-validation/src/main/java/org/openapitools/model/HasOnlyReadOnly.java, line 45:

<comment>The chain/fluent setters `bar(String)` and `foo(String)` are emitted with no Jackson annotation at all, while `access = READ_ONLY` is applied only to the getter and void setter. Whether a readOnly value is refused during request deserialization therefore depends entirely on Jackson merging the fluent setters into the same logical read-only property. Make the guarantee explicit by emitting `@JsonProperty(value = "...", access = JsonProperty.Access.READ_ONLY)` on the chain setters for readOnly vars in JavaSpring/pojo.mustache, so the enforcement is self-documenting and robust.</comment>

<file context>
@@ -42,12 +42,12 @@ public HasOnlyReadOnly bar(@Nullable String bar) {
   
   @Schema(name = "bar", accessMode = Schema.AccessMode.READ_ONLY, requiredMode = Schema.RequiredMode.NOT_REQUIRED)
-  @JsonProperty("bar")
+  @JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY)
   public @Nullable String getBar() {
     return bar;
</file context>


@Schema(name = "bar", accessMode = Schema.AccessMode.READ_ONLY, requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@JsonProperty("bar")
@JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY)

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.

P3: This fix adds READ_ONLY access for readOnly properties, but the Spring generator does not handle the symmetric writeOnly case. The existing JavaJaxRS generators already emit access = JsonProperty.Access.WRITE_ONLY for writeOnly properties, while Spring emits nothing, so writeOnly properties (e.g. passwords, secrets) remain fully serialized into responses. Extend the same conditional handling to writeOnly to match the established JaxRS pattern and avoid leaking write-only data.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/springboot-spring-pageable-delegatePattern/src/main/java/org/openapitools/model/ReadOnlyFirst.java, line 43:

<comment>This fix adds READ_ONLY access for readOnly properties, but the Spring generator does not handle the symmetric writeOnly case. The existing JavaJaxRS generators already emit access = JsonProperty.Access.WRITE_ONLY for writeOnly properties, while Spring emits nothing, so writeOnly properties (e.g. passwords, secrets) remain fully serialized into responses. Extend the same conditional handling to writeOnly to match the established JaxRS pattern and avoid leaking write-only data.</comment>

<file context>
@@ -40,12 +40,12 @@ public ReadOnlyFirst bar(@Nullable String bar) {
   
   @Schema(name = "bar", accessMode = Schema.AccessMode.READ_ONLY, requiredMode = Schema.RequiredMode.NOT_REQUIRED)
-  @JsonProperty("bar")
+  @JsonProperty(value = "bar", access = JsonProperty.Access.READ_ONLY)
   public @Nullable String getBar() {
     return bar;
</file context>

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.

[BUG][JAVA] properties which readOnly: true

1 participant