diff --git a/cli/pom.xml b/cli/pom.xml
index cdc684fe..08c89788 100644
--- a/cli/pom.xml
+++ b/cli/pom.xml
@@ -4,7 +4,7 @@
org.openapitools.openapidiff
openapi-diff-parent
- 2.1.5
+ 2.1.6
openapi-diff-cli
diff --git a/core/pom.xml b/core/pom.xml
index 3856a5fe..3ae4ae7c 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -4,7 +4,7 @@
org.openapitools.openapidiff
openapi-diff-parent
- 2.1.5
+ 2.1.6
openapi-diff-core
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 dd3c8ab3..67d976a3 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
@@ -66,6 +66,7 @@ public , X> DeferredChanged diff(
.setReadOnly(new ChangedReadOnly(left.getReadOnly(), right.getReadOnly(), context))
.setWriteOnly(new ChangedWriteOnly(left.getWriteOnly(), right.getWriteOnly(), context))
.setMaxLength(new ChangedMaxLength(left.getMaxLength(), right.getMaxLength(), context))
+ .setMinLength(new ChangedMinLength(left.getMinLength(), right.getMinLength(), context))
.setNumericRange(
new ChangedNumericRange(
left.getMinimum(),
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/BackwardIncompatibleProp.java b/core/src/main/java/org/openapitools/openapidiff/core/model/BackwardIncompatibleProp.java
index f2180b34..b3da97ca 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/BackwardIncompatibleProp.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/BackwardIncompatibleProp.java
@@ -14,6 +14,7 @@ public enum BackwardIncompatibleProp {
REQUEST_CONTENT_DECREASED("incompatible.request.content.decreased", true),
REQUEST_ENUM_DECREASED("incompatible.request.enum.decreased", true),
REQUEST_MAX_LENGTH_DECREASED("incompatible.request.max.length.decreased", true),
+ REQUEST_MIN_LENGTH_INCREASED("incompatible.request.min.length.increased", false),
REQUEST_NUMERIC_RANGE_DECREASED("incompatible.request.numeric.range.decreased", true),
REQUEST_ONEOF_DECREASED("incompatible.request.oneof.decreased", true),
REQUEST_PARAM_ALLOWEMPTY_DECREASED("incompatible.request.param.allowempty.decreased", true),
@@ -31,6 +32,7 @@ public enum BackwardIncompatibleProp {
RESPONSE_HEADER_REQUIRED_INCREASED("incompatible.response.header.required.increased", true),
RESPONSE_HEADERS_DECREASED("incompatible.response.headers.decreased", true),
RESPONSE_MAX_LENGTH_INCREASED("incompatible.response.max.length.increased", true),
+ RESPONSE_MIN_LENGTH_INCREASED("incompatible.response.min.length.increased", false),
RESPONSE_NUMERIC_RANGE_INCREASED("incompatible.response.numeric.range.increased", false),
RESPONSE_ONEOF_INCREASED("incompatible.response.oneof.increased", true),
RESPONSE_REQUIRED_DECREASED("incompatible.response.required.decreased", true),
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java
index 1896783a..30d126fb 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java
@@ -17,6 +17,7 @@
import org.openapitools.openapidiff.core.model.schema.ChangedMaxLength;
import org.openapitools.openapidiff.core.model.schema.ChangedMaxProperties;
import org.openapitools.openapidiff.core.model.schema.ChangedMinItems;
+import org.openapitools.openapidiff.core.model.schema.ChangedMinLength;
import org.openapitools.openapidiff.core.model.schema.ChangedMinProperties;
import org.openapitools.openapidiff.core.model.schema.ChangedMultipleOf;
import org.openapitools.openapidiff.core.model.schema.ChangedNullable;
@@ -49,6 +50,7 @@ public class ChangedSchema implements ComposedChanged {
protected ChangedWriteOnly writeOnly;
protected boolean changedType;
protected ChangedMaxLength maxLength;
+ protected ChangedMinLength minLength;
protected ChangedNumericRange numericRange;
protected ChangedMultipleOf multipleOf;
protected ChangedMaxItems maxItems;
@@ -139,6 +141,7 @@ public List getChangedElements() {
enumeration,
required,
maxLength,
+ minLength,
numericRange,
multipleOf,
maxItems,
@@ -303,6 +306,10 @@ public ChangedMaxLength getMaxLength() {
return this.maxLength;
}
+ public ChangedMinLength getMinLength() {
+ return this.minLength;
+ }
+
public ChangedNumericRange getNumericRange() {
return this.numericRange;
}
@@ -475,6 +482,12 @@ public ChangedSchema setMaxLength(final ChangedMaxLength maxLength) {
return this;
}
+ public ChangedSchema setMinLength(final ChangedMinLength minLength) {
+ clearChangedCache();
+ this.minLength = minLength;
+ return this;
+ }
+
public ChangedSchema setNumericRange(final ChangedNumericRange numericRange) {
clearChangedCache();
this.numericRange = numericRange;
@@ -585,6 +598,7 @@ public boolean equals(Object o) {
&& Objects.equals(readOnly, that.readOnly)
&& Objects.equals(writeOnly, that.writeOnly)
&& Objects.equals(maxLength, that.maxLength)
+ && Objects.equals(minLength, that.minLength)
&& Objects.equals(numericRange, that.numericRange)
&& Objects.equals(multipleOf, that.multipleOf)
&& Objects.equals(maxItems, that.maxItems)
@@ -623,6 +637,7 @@ public int hashCode() {
writeOnly,
changedType,
maxLength,
+ minLength,
numericRange,
multipleOf,
maxItems,
@@ -681,6 +696,8 @@ public java.lang.String toString() {
+ this.isChangedType()
+ ", maxLength="
+ this.getMaxLength()
+ + ", minLength="
+ + this.getMinLength()
+ ", numericRange="
+ this.getNumericRange()
+ ", multipleOf="
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinLength.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinLength.java
new file mode 100644
index 00000000..a964f4a1
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinLength.java
@@ -0,0 +1,78 @@
+package org.openapitools.openapidiff.core.model.schema;
+
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_MIN_LENGTH_INCREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_MIN_LENGTH_INCREASED;
+
+import java.util.Objects;
+import org.openapitools.openapidiff.core.model.Changed;
+import org.openapitools.openapidiff.core.model.DiffContext;
+import org.openapitools.openapidiff.core.model.DiffResult;
+
+public final class ChangedMinLength implements Changed {
+ private final Integer oldValue;
+ private final Integer newValue;
+ private final DiffContext context;
+
+ @Override
+ public DiffResult isChanged() {
+ if (Objects.equals(oldValue, newValue)) {
+ return DiffResult.NO_CHANGES;
+ }
+ if (context.isRequest() && (newValue != null && (oldValue == null || newValue > oldValue))) {
+ if (REQUEST_MIN_LENGTH_INCREASED.enabled(context)) {
+ return DiffResult.INCOMPATIBLE;
+ }
+ }
+ if (context.isResponse() && (newValue != null && (oldValue == null || newValue > oldValue))) {
+ if (RESPONSE_MIN_LENGTH_INCREASED.enabled(context)) {
+ return DiffResult.INCOMPATIBLE;
+ }
+ }
+ return DiffResult.COMPATIBLE;
+ }
+
+ public ChangedMinLength(
+ final Integer oldValue, final Integer newValue, final DiffContext context) {
+ this.oldValue = oldValue;
+ this.newValue = newValue;
+ this.context = context;
+ }
+
+ public Integer getOldValue() {
+ return this.oldValue;
+ }
+
+ public Integer getNewValue() {
+ return this.newValue;
+ }
+
+ public DiffContext getContext() {
+ return this.context;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ChangedMinLength that = (ChangedMinLength) o;
+ return Objects.equals(oldValue, that.oldValue)
+ && Objects.equals(newValue, that.newValue)
+ && Objects.equals(context, that.context);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(oldValue, newValue, context);
+ }
+
+ @Override
+ public String toString() {
+ return "ChangedMinLength(oldValue="
+ + this.getOldValue()
+ + ", newValue="
+ + this.getNewValue()
+ + ", context="
+ + this.getContext()
+ + ")";
+ }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java b/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java
index e55179b1..b1182606 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java
@@ -51,10 +51,16 @@ public class MarkdownRender implements Render {
public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) {
this.diff = diff;
this.handledSchemas.clear();
+
+ apiTitle(
+ diff.getNewSpecOpenApi().getInfo().getTitle(),
+ diff.getNewSpecOpenApi().getInfo().getVersion(),
+ outputStreamWriter);
listEndpoints("What's New", diff.getNewEndpoints(), outputStreamWriter);
listEndpoints("What's Deleted", diff.getMissingEndpoints(), outputStreamWriter);
listEndpoints("What's Deprecated", diff.getDeprecatedEndpoints(), outputStreamWriter);
listEndpoints(diff.getChangedOperations(), outputStreamWriter);
+ changeSummary("Result", diff, outputStreamWriter);
try {
outputStreamWriter.close();
} catch (IOException e) {
@@ -62,6 +68,29 @@ public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) {
}
}
+ protected String bigTitle(String title) {
+ return H3 + title + '\n' + HR + '\n';
+ }
+
+ protected void apiTitle(String title, String version, OutputStreamWriter outputStreamWriter) {
+ safelyAppend(outputStreamWriter, bigTitle(title + " (v " + version + ")"));
+ }
+
+ protected void changeSummary(
+ String title, ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) {
+ safelyAppend(outputStreamWriter, sectionTitle(title));
+
+ if (diff.isUnchanged()) {
+ safelyAppend(outputStreamWriter, "No differences. Specifications are equivalent");
+ } else if (diff.isCompatible()) {
+ safelyAppend(outputStreamWriter, "API changes are backward compatible");
+ } else {
+ safelyAppend(outputStreamWriter, "API changes broke backward compatibility");
+ }
+
+ safelyAppend(outputStreamWriter, "\n\n");
+ }
+
protected String sectionTitle(String title) {
return H4 + title + '\n' + HR + '\n';
}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/MinLengthBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/MinLengthBCTest.java
new file mode 100644
index 00000000..fbc792b6
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/MinLengthBCTest.java
@@ -0,0 +1,41 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_MIN_LENGTH_INCREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_MIN_LENGTH_INCREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class MinLengthBCTest {
+ private final String BASE = "bc_minlength_base.yaml";
+
+ @Test
+ public void minLengthUnchanged() {
+ assertSpecUnchanged(BASE, BASE);
+ }
+
+ @Test
+ public void requestMinLengthIncreased() {
+ BackwardIncompatibleProp prop = REQUEST_MIN_LENGTH_INCREASED;
+ assertSpecIncompatible(BASE, "bc_request_minlength_increased.yaml", prop);
+ }
+
+ @Test
+ public void requestMinLengthDecreasedButCompatible() {
+ assertSpecChangedButCompatible(BASE, "bc_request_minlength_changed_but_compatible.yaml");
+ }
+
+ @Test
+ public void responseMinLengthIncreased() {
+ BackwardIncompatibleProp prop = RESPONSE_MIN_LENGTH_INCREASED;
+ assertSpecIncompatible(BASE, "bc_response_minlength_increased.yaml", prop);
+ }
+
+ @Test
+ public void responseMinLengthDecreasedButCompatible() {
+ assertSpecChangedButCompatible(BASE, "bc_response_minlength_changed_but_compatible.yaml");
+ }
+}
diff --git a/core/src/test/resources/bc_minlength_base.yaml b/core/src/test/resources/bc_minlength_base.yaml
new file mode 100644
index 00000000..3e61e691
--- /dev/null
+++ b/core/src/test/resources/bc_minlength_base.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.0
+info:
+ description: myDesc
+ title: myTitle
+ version: 1.0.0
+paths:
+ /widgets:
+ post:
+ operationId: widgetCreate
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: string
+ minLength: 5
+ application/xml:
+ schema:
+ type: string
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ type: string
+ minLength: 5
+ application/xml:
+ schema:
+ type: string
+
diff --git a/core/src/test/resources/bc_request_minlength_changed_but_compatible.yaml b/core/src/test/resources/bc_request_minlength_changed_but_compatible.yaml
new file mode 100644
index 00000000..663ee7c0
--- /dev/null
+++ b/core/src/test/resources/bc_request_minlength_changed_but_compatible.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.0
+info:
+ description: myDesc
+ title: myTitle
+ version: 1.0.0
+paths:
+ /widgets:
+ post:
+ operationId: widgetCreate
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: string
+ minLength: 3
+ application/xml:
+ schema:
+ type: string
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ type: string
+ minLength: 5
+ application/xml:
+ schema:
+ type: string
+
diff --git a/core/src/test/resources/bc_request_minlength_increased.yaml b/core/src/test/resources/bc_request_minlength_increased.yaml
new file mode 100644
index 00000000..f7760ba6
--- /dev/null
+++ b/core/src/test/resources/bc_request_minlength_increased.yaml
@@ -0,0 +1,31 @@
+openapi: 3.0.0
+info:
+ description: myDesc
+ title: myTitle
+ version: 1.0.0
+paths:
+ /widgets:
+ post:
+ operationId: widgetCreate
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: string
+ minLength: 10
+ application/xml:
+ schema:
+ type: string
+ minLength: 10
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ type: string
+ minLength: 5
+ application/xml:
+ schema:
+ type: string
+
diff --git a/core/src/test/resources/bc_response_minlength_changed_but_compatible.yaml b/core/src/test/resources/bc_response_minlength_changed_but_compatible.yaml
new file mode 100644
index 00000000..a965540a
--- /dev/null
+++ b/core/src/test/resources/bc_response_minlength_changed_but_compatible.yaml
@@ -0,0 +1,31 @@
+openapi: 3.0.0
+info:
+ description: myDesc
+ title: myTitle
+ version: 1.0.0
+paths:
+ /widgets:
+ post:
+ operationId: widgetCreate
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: string
+ minLength: 5
+ application/xml:
+ schema:
+ type: string
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ type: string
+ minLength: 3
+ application/xml:
+ schema:
+ type: string
+ minLength: 3
+
diff --git a/core/src/test/resources/bc_response_minlength_increased.yaml b/core/src/test/resources/bc_response_minlength_increased.yaml
new file mode 100644
index 00000000..06197c03
--- /dev/null
+++ b/core/src/test/resources/bc_response_minlength_increased.yaml
@@ -0,0 +1,31 @@
+openapi: 3.0.0
+info:
+ description: myDesc
+ title: myTitle
+ version: 1.0.0
+paths:
+ /widgets:
+ post:
+ operationId: widgetCreate
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: string
+ minLength: 5
+ application/xml:
+ schema:
+ type: string
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ type: string
+ minLength: 10
+ application/xml:
+ schema:
+ type: string
+ minLength: 10
+
diff --git a/maven-example/pom.xml b/maven-example/pom.xml
index b6938325..615b0024 100644
--- a/maven-example/pom.xml
+++ b/maven-example/pom.xml
@@ -3,7 +3,7 @@
openapi-diff-parent
org.openapitools.openapidiff
- 2.1.5
+ 2.1.6
4.0.0
@@ -23,7 +23,7 @@
org.openapitools.openapidiff
openapi-diff-maven
- 2.1.5
+ 2.1.6
diff --git a/maven/pom.xml b/maven/pom.xml
index 79a072c4..9c631c78 100644
--- a/maven/pom.xml
+++ b/maven/pom.xml
@@ -5,7 +5,7 @@
org.openapitools.openapidiff
openapi-diff-parent
- 2.1.5
+ 2.1.6
openapi-diff-maven
@@ -56,7 +56,7 @@
org.apache.maven.plugins
maven-plugin-plugin
- 3.13.1
+ 3.15.2
openapi-diff
diff --git a/pom.xml b/pom.xml
index 6187f0f0..3bade228 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
org.openapitools.openapidiff
openapi-diff-parent
- 2.1.5
+ 2.1.6
pom
openapi-diff-parent
@@ -82,7 +82,7 @@
1.8
UTF-8
UTF-8
- 2025-11-03T08:59:15Z
+ 2025-11-26T14:07:20Z
github
openapitools
@@ -99,12 +99,12 @@
org.openapitools.openapidiff
openapi-diff-core
- 2.1.5
+ 2.1.6
org.junit
junit-bom
- 5.13.4
+ 5.14.1
pom
import
@@ -141,7 +141,7 @@
commons-cli
commons-cli
- 1.10.0
+ 1.11.0
org.apache.commons
@@ -304,7 +304,7 @@
org.apache.maven.plugins
maven-release-plugin
- 3.1.1
+ 3.2.0
org.apache.maven.plugins
@@ -354,7 +354,7 @@
org.sonarsource.scanner.maven
sonar-maven-plugin
- 5.2.0.4988
+ 5.3.0.6276