From 22b12c866c97c324fe064d2707e77bae94029c3a Mon Sep 17 00:00:00 2001 From: Inf1mum Date: Sat, 7 Nov 2026 20:41:51 +0300 Subject: [PATCH 1/2] Fix #357: ignore changes inside readOnly properties in requests Clients never send readOnly properties, so changes nested inside a readOnly property of a requestBody cannot affect the request contract. The property-level diff already filtered added/removed readOnly properties (isPropertyApplicable), but changes inside a shared readOnly property were still diffed: the nested changed schemas were reported in ChangedOpenApi#getChangedSchemas and flagged the diff backward incompatible, while the rendered report showed nothing. Skip the schema diff for shared properties that are marked readOnly on both sides in a request context, mirroring the writeOnly handling for responses. A change of the readOnly flag itself still recurses so the existing REQUEST_READONLY_* back-compat checks keep working. Response side behavior is unchanged. --- .../schemadiffresult/SchemaDiffResult.java | 13 +++ .../openapidiff/core/Issue357Test.java | 46 +++++++++++ .../issue-357-readonly-response_1.json | 50 ++++++++++++ .../issue-357-readonly-response_2.json | 50 ++++++++++++ .../issue-357-writeonly-response_1.json | 50 ++++++++++++ .../issue-357-writeonly-response_2.json | 50 ++++++++++++ core/src/test/resources/issue-357_1.json | 79 +++++++++++++++++++ core/src/test/resources/issue-357_2.json | 79 +++++++++++++++++++ 8 files changed, 417 insertions(+) create mode 100644 core/src/test/java/org/openapitools/openapidiff/core/Issue357Test.java create mode 100644 core/src/test/resources/issue-357-readonly-response_1.json create mode 100644 core/src/test/resources/issue-357-readonly-response_2.json create mode 100644 core/src/test/resources/issue-357-writeonly-response_1.json create mode 100644 core/src/test/resources/issue-357-writeonly-response_2.json create mode 100644 core/src/test/resources/issue-357_1.json create mode 100644 core/src/test/resources/issue-357_2.json diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java index 67d976a3..be77bfb4 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java @@ -106,6 +106,12 @@ public , X> DeferredChanged diff( Map rightProperties = right.getProperties(); MapKeyDiff propertyDiff = MapKeyDiff.diff(leftProperties, rightProperties); for (String key : propertyDiff.getSharedKey()) { + if (isReadOnlyPropertyInRequest(leftProperties.get(key), rightProperties.get(key), context)) { + // Changes inside a readOnly property cannot affect the request contract, since + // clients never send readOnly properties, so the property is compared as unchanged. + // When the readOnly flag itself differs, one side is not readOnly and is still compared. + continue; + } builder .with( openApiDiff @@ -163,6 +169,13 @@ private boolean isPropertyApplicable(Schema schema, DiffContext context) { && !(context.isRequest() && Boolean.TRUE.equals(schema.getReadOnly())); } + private boolean isReadOnlyPropertyInRequest( + Schema leftProperty, Schema rightProperty, DiffContext context) { + return context.isRequest() + && Boolean.TRUE.equals(leftProperty.getReadOnly()) + && Boolean.TRUE.equals(rightProperty.getReadOnly()); + } + private DiffContext required(DiffContext context, String key, List required) { return context.copyWithRequired(required != null && required.contains(key)); } diff --git a/core/src/test/java/org/openapitools/openapidiff/core/Issue357Test.java b/core/src/test/java/org/openapitools/openapidiff/core/Issue357Test.java new file mode 100644 index 00000000..b9323360 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/Issue357Test.java @@ -0,0 +1,46 @@ +package org.openapitools.openapidiff.core; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; + +public class Issue357Test { + + private final String READONLY_REQUEST_OLD = "issue-357_1.json"; + private final String READONLY_REQUEST_NEW = "issue-357_2.json"; + private final String WRITEONLY_RESPONSE_OLD = "issue-357-writeonly-response_1.json"; + private final String WRITEONLY_RESPONSE_NEW = "issue-357-writeonly-response_2.json"; + private final String READONLY_RESPONSE_OLD = "issue-357-readonly-response_1.json"; + private final String READONLY_RESPONSE_NEW = "issue-357-readonly-response_2.json"; + + @Test // issue #357 + public void changesInsideReadOnlyPropertyInRequestAreIgnored() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations(READONLY_REQUEST_OLD, READONLY_REQUEST_NEW); + + assertThat(changedOpenApi.getChangedOperations()).isEmpty(); + assertThat(changedOpenApi.getChangedSchemas()).isEmpty(); + assertThat(changedOpenApi.isUnchanged()).isTrue(); + } + + @Test // counter-case: writeOnly in a response is still reported + public void changesInsideWriteOnlyPropertyInResponseAreStillReported() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations(WRITEONLY_RESPONSE_OLD, WRITEONLY_RESPONSE_NEW); + + assertThat(changedOpenApi.getChangedSchemas()).isNotEmpty(); + assertThat(changedOpenApi.isChanged().isDifferent()).isTrue(); + assertThat(changedOpenApi.isIncompatible()).isTrue(); + } + + @Test // response-side behavior must stay unchanged: readOnly in a response is still reported + public void changesInsideReadOnlyPropertyInResponseAreStillReported() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations(READONLY_RESPONSE_OLD, READONLY_RESPONSE_NEW); + + assertThat(changedOpenApi.getChangedOperations()).isNotEmpty(); + assertThat(changedOpenApi.isChanged().isDifferent()).isTrue(); + assertThat(changedOpenApi.isIncompatible()).isTrue(); + } +} diff --git a/core/src/test/resources/issue-357-readonly-response_1.json b/core/src/test/resources/issue-357-readonly-response_1.json new file mode 100644 index 00000000..fa371c99 --- /dev/null +++ b/core/src/test/resources/issue-357-readonly-response_1.json @@ -0,0 +1,50 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "paths": { + "/resource": { + "get": { + "responses": { + "200": { + "description": "Fetched resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceResponse" + } + } + } + } + }, + "summary": "Fetch resource" + } + } + }, + "components": { + "schemas": { + "ResourceResponse": { + "type": "object", + "properties": { + "ro": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Deep" + }, + "readOnly": true + } + } + }, + "Deep": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + } + } + } +} diff --git a/core/src/test/resources/issue-357-readonly-response_2.json b/core/src/test/resources/issue-357-readonly-response_2.json new file mode 100644 index 00000000..f10b8232 --- /dev/null +++ b/core/src/test/resources/issue-357-readonly-response_2.json @@ -0,0 +1,50 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "paths": { + "/resource": { + "get": { + "responses": { + "200": { + "description": "Fetched resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceResponse" + } + } + } + } + }, + "summary": "Fetch resource" + } + } + }, + "components": { + "schemas": { + "ResourceResponse": { + "type": "object", + "properties": { + "ro": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Deep" + }, + "readOnly": true + } + } + }, + "Deep": { + "type": "object", + "properties": { + "name": { + "type": "integer" + } + } + } + } + } +} diff --git a/core/src/test/resources/issue-357-writeonly-response_1.json b/core/src/test/resources/issue-357-writeonly-response_1.json new file mode 100644 index 00000000..e98573a6 --- /dev/null +++ b/core/src/test/resources/issue-357-writeonly-response_1.json @@ -0,0 +1,50 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "paths": { + "/resource": { + "get": { + "responses": { + "200": { + "description": "Fetched resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceResponse" + } + } + } + } + }, + "summary": "Fetch resource" + } + } + }, + "components": { + "schemas": { + "ResourceResponse": { + "type": "object", + "properties": { + "wo": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Deep" + }, + "writeOnly": true + } + } + }, + "Deep": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + } + } + } +} diff --git a/core/src/test/resources/issue-357-writeonly-response_2.json b/core/src/test/resources/issue-357-writeonly-response_2.json new file mode 100644 index 00000000..7544d51f --- /dev/null +++ b/core/src/test/resources/issue-357-writeonly-response_2.json @@ -0,0 +1,50 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "paths": { + "/resource": { + "get": { + "responses": { + "200": { + "description": "Fetched resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResourceResponse" + } + } + } + } + }, + "summary": "Fetch resource" + } + } + }, + "components": { + "schemas": { + "ResourceResponse": { + "type": "object", + "properties": { + "wo": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Deep" + }, + "writeOnly": true + } + } + }, + "Deep": { + "type": "object", + "properties": { + "name": { + "type": "integer" + } + } + } + } + } +} diff --git a/core/src/test/resources/issue-357_1.json b/core/src/test/resources/issue-357_1.json new file mode 100644 index 00000000..8d43ba0d --- /dev/null +++ b/core/src/test/resources/issue-357_1.json @@ -0,0 +1,79 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "paths": { + "/resource": { + "post": { + "responses": { + "200": { + "description": "Created resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceResponse" + } + } + } + } + }, + "summary": "Create resource", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceRequest" + } + } + }, + "description": "Definition of the resource" + } + } + } + }, + "components": { + "schemas": { + "CreateResourceResponse": { + "type": "object", + "properties": { + "resources": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CreateResourceRequest": { + "type": "object", + "properties": { + "foo": { + "$ref": "#/components/schemas/Foo" + } + } + }, + "Foo": { + "type": "object", + "properties": { + "bar": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Bar" + }, + "readOnly": true + } + } + }, + "Bar": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + } + } + } +} diff --git a/core/src/test/resources/issue-357_2.json b/core/src/test/resources/issue-357_2.json new file mode 100644 index 00000000..d6c88c9e --- /dev/null +++ b/core/src/test/resources/issue-357_2.json @@ -0,0 +1,79 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "paths": { + "/resource": { + "post": { + "responses": { + "200": { + "description": "Created resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceResponse" + } + } + } + } + }, + "summary": "Create resource", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceRequest" + } + } + }, + "description": "Definition of the resource" + } + } + } + }, + "components": { + "schemas": { + "CreateResourceResponse": { + "type": "object", + "properties": { + "resources": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CreateResourceRequest": { + "type": "object", + "properties": { + "foo": { + "$ref": "#/components/schemas/Foo" + } + } + }, + "Foo": { + "type": "object", + "properties": { + "bar": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Bar" + }, + "readOnly": true + } + } + }, + "Bar": { + "type": "object", + "properties": { + "name": { + "type": "integer" + } + } + } + } + } +} From 97990c15929bbc6836c0677dece24f5ee1bd569e Mon Sep 17 00:00:00 2001 From: Inf1mum Date: Sun, 8 Nov 2026 00:24:40 +0300 Subject: [PATCH 2/2] fix: drop required diff for skipped readOnly properties and resolve refs in requests Address cubic review feedback on #934: - Remove the skipped readOnly property from the parent `required` diff (both increased and missing entries) so adding a response-only field to `required` can no longer flag the request as breaking. - Resolve `$ref`-referenced shared properties before checking the readOnly flag; the unresolved wrapper carries no readOnly flag, so changes inside referenced readOnly component schemas were still reported in request diffs. - Add fixtures/tests: readOnly property added to parent `required`, readOnly property referenced via `$ref`, and readOnly flag transitions (true -> false and false -> true) asserting the flag change and nested changes are still reported and never swallowed by the skip logic. Signed-off-by: Inf1mum --- .../schemadiffresult/SchemaDiffResult.java | 36 +++++++-- .../openapidiff/core/Issue357Test.java | 58 ++++++++++++++ .../resources/issue-357-readonly-ref_1.json | 68 ++++++++++++++++ .../resources/issue-357-readonly-ref_2.json | 69 ++++++++++++++++ .../issue-357-readonly-required_1.json | 79 ++++++++++++++++++ .../issue-357-readonly-required_2.json | 80 +++++++++++++++++++ .../issue-357-readonly-transition-off_1.json | 79 ++++++++++++++++++ .../issue-357-readonly-transition-off_2.json | 79 ++++++++++++++++++ .../issue-357-readonly-transition-on_1.json | 79 ++++++++++++++++++ .../issue-357-readonly-transition-on_2.json | 79 ++++++++++++++++++ 10 files changed, 701 insertions(+), 5 deletions(-) create mode 100644 core/src/test/resources/issue-357-readonly-ref_1.json create mode 100644 core/src/test/resources/issue-357-readonly-ref_2.json create mode 100644 core/src/test/resources/issue-357-readonly-required_1.json create mode 100644 core/src/test/resources/issue-357-readonly-required_2.json create mode 100644 core/src/test/resources/issue-357-readonly-transition-off_1.json create mode 100644 core/src/test/resources/issue-357-readonly-transition-off_2.json create mode 100644 core/src/test/resources/issue-357-readonly-transition-on_1.json create mode 100644 core/src/test/resources/issue-357-readonly-transition-on_2.json diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java index be77bfb4..d0d7dd6f 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java @@ -25,8 +25,12 @@ import org.openapitools.openapidiff.core.model.schema.ChangedReadOnly; import org.openapitools.openapidiff.core.model.schema.ChangedRequired; import org.openapitools.openapidiff.core.model.schema.ChangedUniqueItems; +import org.openapitools.openapidiff.core.utils.RefPointer; +import org.openapitools.openapidiff.core.utils.RefType; public class SchemaDiffResult { + private static final RefPointer> refPointer = new RefPointer<>(RefType.SCHEMAS); + protected ChangedSchema changedSchema; protected OpenApiDiff openApiDiff; @@ -106,7 +110,17 @@ public , X> DeferredChanged diff( Map rightProperties = right.getProperties(); MapKeyDiff propertyDiff = MapKeyDiff.diff(leftProperties, rightProperties); for (String key : propertyDiff.getSharedKey()) { - if (isReadOnlyPropertyInRequest(leftProperties.get(key), rightProperties.get(key), context)) { + if (isReadOnlyPropertyInRequest( + leftComponents, + rightComponents, + leftProperties.get(key), + rightProperties.get(key), + context)) { + // The property is treated as unchanged, so any parent `required` change for it is + // dropped as well; otherwise adding a response-only field to `required` would still + // flag the request as breaking. + changedSchema.getRequired().getIncreased().remove(key); + changedSchema.getRequired().getMissing().remove(key); // Changes inside a readOnly property cannot affect the request contract, since // clients never send readOnly properties, so the property is compared as unchanged. // When the readOnly flag itself differs, one side is not readOnly and is still compared. @@ -170,10 +184,22 @@ private boolean isPropertyApplicable(Schema schema, DiffContext context) { } private boolean isReadOnlyPropertyInRequest( - Schema leftProperty, Schema rightProperty, DiffContext context) { - return context.isRequest() - && Boolean.TRUE.equals(leftProperty.getReadOnly()) - && Boolean.TRUE.equals(rightProperty.getReadOnly()); + Components leftComponents, + Components rightComponents, + Schema leftProperty, + Schema rightProperty, + DiffContext context) { + if (!context.isRequest() || leftProperty == null || rightProperty == null) { + return false; + } + // Properties may be `$ref` wrappers without a readOnly flag of their own, so resolve + // the refs before checking the flag. + Schema leftResolved = + refPointer.resolveRef(leftComponents, leftProperty, leftProperty.get$ref()); + Schema rightResolved = + refPointer.resolveRef(rightComponents, rightProperty, rightProperty.get$ref()); + return Boolean.TRUE.equals(leftResolved.getReadOnly()) + && Boolean.TRUE.equals(rightResolved.getReadOnly()); } private DiffContext required(DiffContext context, String key, List required) { diff --git a/core/src/test/java/org/openapitools/openapidiff/core/Issue357Test.java b/core/src/test/java/org/openapitools/openapidiff/core/Issue357Test.java index b9323360..39cb02d4 100644 --- a/core/src/test/java/org/openapitools/openapidiff/core/Issue357Test.java +++ b/core/src/test/java/org/openapitools/openapidiff/core/Issue357Test.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import org.openapitools.openapidiff.core.model.ChangedOpenApi; +import org.openapitools.openapidiff.core.model.ChangedSchema; public class Issue357Test { @@ -13,6 +14,14 @@ public class Issue357Test { private final String WRITEONLY_RESPONSE_NEW = "issue-357-writeonly-response_2.json"; private final String READONLY_RESPONSE_OLD = "issue-357-readonly-response_1.json"; private final String READONLY_RESPONSE_NEW = "issue-357-readonly-response_2.json"; + private final String READONLY_REQUIRED_OLD = "issue-357-readonly-required_1.json"; + private final String READONLY_REQUIRED_NEW = "issue-357-readonly-required_2.json"; + private final String READONLY_REF_OLD = "issue-357-readonly-ref_1.json"; + private final String READONLY_REF_NEW = "issue-357-readonly-ref_2.json"; + private final String READONLY_TRANSITION_OFF_OLD = "issue-357-readonly-transition-off_1.json"; + private final String READONLY_TRANSITION_OFF_NEW = "issue-357-readonly-transition-off_2.json"; + private final String READONLY_TRANSITION_ON_OLD = "issue-357-readonly-transition-on_1.json"; + private final String READONLY_TRANSITION_ON_NEW = "issue-357-readonly-transition-on_2.json"; @Test // issue #357 public void changesInsideReadOnlyPropertyInRequestAreIgnored() { @@ -24,6 +33,55 @@ public void changesInsideReadOnlyPropertyInRequestAreIgnored() { assertThat(changedOpenApi.isUnchanged()).isTrue(); } + @Test // changes to the parent `required` list of a skipped readOnly property are ignored too + public void requiredChangeForReadOnlyPropertyInRequestIsIgnored() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations(READONLY_REQUIRED_OLD, READONLY_REQUIRED_NEW); + + assertThat(changedOpenApi.getChangedOperations()).isEmpty(); + assertThat(changedOpenApi.getChangedSchemas()).isEmpty(); + assertThat(changedOpenApi.isUnchanged()).isTrue(); + } + + @Test // a property referenced via `$ref` that resolves to a readOnly schema is ignored too, + // including parent `required` changes for the referenced property + public void changesInsideReferencedReadOnlyPropertyInRequestAreIgnored() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations(READONLY_REF_OLD, READONLY_REF_NEW); + + assertThat(changedOpenApi.getChangedOperations()).isEmpty(); + assertThat(changedOpenApi.getChangedSchemas()).isEmpty(); + assertThat(changedOpenApi.isUnchanged()).isTrue(); + } + + @Test // readOnly true -> false: the flag change is a real wire change and must be reported + public void readOnlyFlagRemovedInRequestStillReportsNestedChanges() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations(READONLY_TRANSITION_OFF_OLD, READONLY_TRANSITION_OFF_NEW); + + assertThat(changedOpenApi.getChangedSchemas()).isNotEmpty(); + assertThat(changedOpenApi.isChanged().isDifferent()).isTrue(); + assertThat(changedOpenApi.isIncompatible()).isTrue(); + assertThat(anyNestedTypeChange(changedOpenApi)).isTrue(); + } + + @Test // readOnly false -> true: the flag change is a real wire change and must be reported + public void readOnlyFlagAddedInRequestStillReportsNestedChanges() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations(READONLY_TRANSITION_ON_OLD, READONLY_TRANSITION_ON_NEW); + + assertThat(changedOpenApi.getChangedSchemas()).isNotEmpty(); + assertThat(changedOpenApi.isChanged().isDifferent()).isTrue(); + assertThat(changedOpenApi.isIncompatible()).isTrue(); + assertThat(anyNestedTypeChange(changedOpenApi)).isTrue(); + } + + private static boolean anyNestedTypeChange(ChangedOpenApi changedOpenApi) { + return changedOpenApi.getChangedSchemas().stream() + .flatMap(changedSchema -> changedSchema.getChangedProperties().values().stream()) + .anyMatch(ChangedSchema::isChangedType); + } + @Test // counter-case: writeOnly in a response is still reported public void changesInsideWriteOnlyPropertyInResponseAreStillReported() { ChangedOpenApi changedOpenApi = diff --git a/core/src/test/resources/issue-357-readonly-ref_1.json b/core/src/test/resources/issue-357-readonly-ref_1.json new file mode 100644 index 00000000..820737ef --- /dev/null +++ b/core/src/test/resources/issue-357-readonly-ref_1.json @@ -0,0 +1,68 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "paths": { + "/resource": { + "post": { + "responses": { + "200": { + "description": "Created resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceResponse" + } + } + } + } + }, + "summary": "Create resource", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceRequest" + } + } + }, + "description": "Definition of the resource" + } + } + } + }, + "components": { + "schemas": { + "CreateResourceResponse": { + "type": "object", + "properties": { + "resources": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CreateResourceRequest": { + "type": "object", + "properties": { + "foo": { + "$ref": "#/components/schemas/Foo" + } + } + }, + "Foo": { + "type": "object", + "readOnly": true, + "properties": { + "bar": { + "type": "string" + } + } + } + } + } +} diff --git a/core/src/test/resources/issue-357-readonly-ref_2.json b/core/src/test/resources/issue-357-readonly-ref_2.json new file mode 100644 index 00000000..70eb4c58 --- /dev/null +++ b/core/src/test/resources/issue-357-readonly-ref_2.json @@ -0,0 +1,69 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "paths": { + "/resource": { + "post": { + "responses": { + "200": { + "description": "Created resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceResponse" + } + } + } + } + }, + "summary": "Create resource", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceRequest" + } + } + }, + "description": "Definition of the resource" + } + } + } + }, + "components": { + "schemas": { + "CreateResourceResponse": { + "type": "object", + "properties": { + "resources": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CreateResourceRequest": { + "type": "object", + "required": ["foo"], + "properties": { + "foo": { + "$ref": "#/components/schemas/Foo" + } + } + }, + "Foo": { + "type": "object", + "readOnly": true, + "properties": { + "bar": { + "type": "integer" + } + } + } + } + } +} diff --git a/core/src/test/resources/issue-357-readonly-required_1.json b/core/src/test/resources/issue-357-readonly-required_1.json new file mode 100644 index 00000000..8d43ba0d --- /dev/null +++ b/core/src/test/resources/issue-357-readonly-required_1.json @@ -0,0 +1,79 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "paths": { + "/resource": { + "post": { + "responses": { + "200": { + "description": "Created resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceResponse" + } + } + } + } + }, + "summary": "Create resource", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceRequest" + } + } + }, + "description": "Definition of the resource" + } + } + } + }, + "components": { + "schemas": { + "CreateResourceResponse": { + "type": "object", + "properties": { + "resources": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CreateResourceRequest": { + "type": "object", + "properties": { + "foo": { + "$ref": "#/components/schemas/Foo" + } + } + }, + "Foo": { + "type": "object", + "properties": { + "bar": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Bar" + }, + "readOnly": true + } + } + }, + "Bar": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + } + } + } +} diff --git a/core/src/test/resources/issue-357-readonly-required_2.json b/core/src/test/resources/issue-357-readonly-required_2.json new file mode 100644 index 00000000..c27cccde --- /dev/null +++ b/core/src/test/resources/issue-357-readonly-required_2.json @@ -0,0 +1,80 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "paths": { + "/resource": { + "post": { + "responses": { + "200": { + "description": "Created resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceResponse" + } + } + } + } + }, + "summary": "Create resource", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceRequest" + } + } + }, + "description": "Definition of the resource" + } + } + } + }, + "components": { + "schemas": { + "CreateResourceResponse": { + "type": "object", + "properties": { + "resources": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CreateResourceRequest": { + "type": "object", + "properties": { + "foo": { + "$ref": "#/components/schemas/Foo" + } + } + }, + "Foo": { + "type": "object", + "required": ["bar"], + "properties": { + "bar": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Bar" + }, + "readOnly": true + } + } + }, + "Bar": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + } + } + } +} diff --git a/core/src/test/resources/issue-357-readonly-transition-off_1.json b/core/src/test/resources/issue-357-readonly-transition-off_1.json new file mode 100644 index 00000000..8d43ba0d --- /dev/null +++ b/core/src/test/resources/issue-357-readonly-transition-off_1.json @@ -0,0 +1,79 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "paths": { + "/resource": { + "post": { + "responses": { + "200": { + "description": "Created resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceResponse" + } + } + } + } + }, + "summary": "Create resource", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceRequest" + } + } + }, + "description": "Definition of the resource" + } + } + } + }, + "components": { + "schemas": { + "CreateResourceResponse": { + "type": "object", + "properties": { + "resources": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CreateResourceRequest": { + "type": "object", + "properties": { + "foo": { + "$ref": "#/components/schemas/Foo" + } + } + }, + "Foo": { + "type": "object", + "properties": { + "bar": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Bar" + }, + "readOnly": true + } + } + }, + "Bar": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + } + } + } +} diff --git a/core/src/test/resources/issue-357-readonly-transition-off_2.json b/core/src/test/resources/issue-357-readonly-transition-off_2.json new file mode 100644 index 00000000..e7a8bdb2 --- /dev/null +++ b/core/src/test/resources/issue-357-readonly-transition-off_2.json @@ -0,0 +1,79 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "paths": { + "/resource": { + "post": { + "responses": { + "200": { + "description": "Created resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceResponse" + } + } + } + } + }, + "summary": "Create resource", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceRequest" + } + } + }, + "description": "Definition of the resource" + } + } + } + }, + "components": { + "schemas": { + "CreateResourceResponse": { + "type": "object", + "properties": { + "resources": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CreateResourceRequest": { + "type": "object", + "properties": { + "foo": { + "$ref": "#/components/schemas/Foo" + } + } + }, + "Foo": { + "type": "object", + "properties": { + "bar": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Bar" + }, + "readOnly": false + } + } + }, + "Bar": { + "type": "object", + "properties": { + "name": { + "type": "integer" + } + } + } + } + } +} diff --git a/core/src/test/resources/issue-357-readonly-transition-on_1.json b/core/src/test/resources/issue-357-readonly-transition-on_1.json new file mode 100644 index 00000000..2e80a978 --- /dev/null +++ b/core/src/test/resources/issue-357-readonly-transition-on_1.json @@ -0,0 +1,79 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "paths": { + "/resource": { + "post": { + "responses": { + "200": { + "description": "Created resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceResponse" + } + } + } + } + }, + "summary": "Create resource", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceRequest" + } + } + }, + "description": "Definition of the resource" + } + } + } + }, + "components": { + "schemas": { + "CreateResourceResponse": { + "type": "object", + "properties": { + "resources": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CreateResourceRequest": { + "type": "object", + "properties": { + "foo": { + "$ref": "#/components/schemas/Foo" + } + } + }, + "Foo": { + "type": "object", + "properties": { + "bar": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Bar" + }, + "readOnly": false + } + } + }, + "Bar": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + } + } + } +} diff --git a/core/src/test/resources/issue-357-readonly-transition-on_2.json b/core/src/test/resources/issue-357-readonly-transition-on_2.json new file mode 100644 index 00000000..d6c88c9e --- /dev/null +++ b/core/src/test/resources/issue-357-readonly-transition-on_2.json @@ -0,0 +1,79 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "paths": { + "/resource": { + "post": { + "responses": { + "200": { + "description": "Created resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceResponse" + } + } + } + } + }, + "summary": "Create resource", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateResourceRequest" + } + } + }, + "description": "Definition of the resource" + } + } + } + }, + "components": { + "schemas": { + "CreateResourceResponse": { + "type": "object", + "properties": { + "resources": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CreateResourceRequest": { + "type": "object", + "properties": { + "foo": { + "$ref": "#/components/schemas/Foo" + } + } + }, + "Foo": { + "type": "object", + "properties": { + "bar": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Bar" + }, + "readOnly": true + } + } + }, + "Bar": { + "type": "object", + "properties": { + "name": { + "type": "integer" + } + } + } + } + } +}