From 5270cf11bc0a5e1284fd3e853a81135fbfe4a5d5 Mon Sep 17 00:00:00 2001 From: Aliaksandr Pinchuk Date: Thu, 27 Feb 2025 23:37:21 +0100 Subject: [PATCH 1/2] Changes on 'minItems' and 'maxItems' not detected Fixes #480 --- .../schemadiffresult/SchemaDiffResult.java | 6 ++- .../openapidiff/core/model/ChangedSchema.java | 34 +++++++++++++++ .../core/model/schema/ChangedMaxItems.java | 43 +++++++++++++++++++ .../core/model/schema/ChangedMinItems.java | 43 +++++++++++++++++++ .../openapidiff/core/SchemaDiffTest.java | 35 +++++++++++++++ .../schema-min-max-items-diff-1.yaml | 42 ++++++++++++++++++ .../schema-min-max-items-diff-2.yaml | 42 ++++++++++++++++++ 7 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxItems.java create mode 100644 core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinItems.java create mode 100644 core/src/test/resources/schemaDiff/schema-min-max-items-diff-1.yaml create mode 100644 core/src/test/resources/schemaDiff/schema-min-max-items-diff-2.yaml 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 f3b1d44c..db10309c 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 @@ -19,6 +19,8 @@ import org.openapitools.openapidiff.core.model.deferred.DeferredChanged; import org.openapitools.openapidiff.core.model.deferred.RecursiveSchemaSet; import org.openapitools.openapidiff.core.model.schema.*; +import org.openapitools.openapidiff.core.model.schema.ChangedMaxItems; +import org.openapitools.openapidiff.core.model.schema.ChangedMinItems; public class SchemaDiffResult { protected ChangedSchema changedSchema; @@ -73,7 +75,9 @@ public , X> DeferredChanged diff( context)) .setMultipleOf(new ChangedMultipleOf(left.getMultipleOf(), right.getMultipleOf())) .setExamples(new ChangedExamples(left.getExamples(), right.getExamples())) - .setExample(new ChangedExample(left.getExample(), right.getExample())); + .setExample(new ChangedExample(left.getExample(), right.getExample())) + .setMaxItems(new ChangedMaxItems(left.getMaxItems(), right.getMaxItems(), context)) + .setMinItems(new ChangedMinItems(left.getMinItems(), right.getMinItems(), context)); builder .with( openApiDiff 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 d11575ad..1c1cfc53 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 @@ -15,6 +15,8 @@ import org.openapitools.openapidiff.core.model.schema.ChangedReadOnly; import org.openapitools.openapidiff.core.model.schema.ChangedRequired; import org.openapitools.openapidiff.core.model.schema.ChangedWriteOnly; +import org.openapitools.openapidiff.core.model.schema.ChangedMaxItems; +import org.openapitools.openapidiff.core.model.schema.ChangedMinItems; public class ChangedSchema implements ComposedChanged { protected DiffContext context; @@ -39,6 +41,8 @@ public class ChangedSchema implements ComposedChanged { protected ChangedMaxLength maxLength; protected ChangedNumericRange numericRange; protected ChangedMultipleOf multipleOf; + protected ChangedMaxItems maxItems; + protected ChangedMinItems minItems; protected boolean discriminatorPropertyChanged; protected ChangedSchema items; protected ChangedOneOfSchema oneOfSchema; @@ -122,6 +126,8 @@ public List getChangedElements() { maxLength, numericRange, multipleOf, + maxItems, + minItems, extensions)) .collect(Collectors.toList()); } @@ -285,6 +291,14 @@ public ChangedMultipleOf getMultipleOf() { return this.multipleOf; } + public ChangedMaxItems getMaxItems() { + return this.maxItems; + } + + public ChangedMinItems getMinItems() { + return this.minItems; + } + public boolean isDiscriminatorPropertyChanged() { return this.discriminatorPropertyChanged; } @@ -433,6 +447,18 @@ public ChangedSchema setMultipleOf(final ChangedMultipleOf multipleOf) { return this; } + public ChangedSchema setMaxItems(final ChangedMaxItems maxItems) { + clearChangedCache(); + this.maxItems = maxItems; + return this; + } + + public ChangedSchema setMinItems(final ChangedMinItems minItems) { + clearChangedCache(); + this.minItems = minItems; + return this; + } + public ChangedSchema setDiscriminatorPropertyChanged(final boolean discriminatorPropertyChanged) { clearChangedCache(); this.discriminatorPropertyChanged = discriminatorPropertyChanged; @@ -491,6 +517,8 @@ public boolean equals(Object o) { && Objects.equals(maxLength, that.maxLength) && Objects.equals(numericRange, that.numericRange) && Objects.equals(multipleOf, that.multipleOf) + && Objects.equals(maxItems, that.maxItems) + && Objects.equals(minItems, that.minItems) && Objects.equals(items, that.items) && Objects.equals(oneOfSchema, that.oneOfSchema) && Objects.equals(addProp, that.addProp) @@ -522,6 +550,8 @@ public int hashCode() { maxLength, numericRange, multipleOf, + maxItems, + minItems, discriminatorPropertyChanged, items, oneOfSchema, @@ -575,6 +605,10 @@ public java.lang.String toString() { + this.getNumericRange() + ", multipleOf=" + this.getMultipleOf() + + ", maxItems=" + + this.getMaxItems() + + ", minItems=" + + this.getMinItems() + ", discriminatorPropertyChanged=" + this.isDiscriminatorPropertyChanged() + ", items=" diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxItems.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxItems.java new file mode 100644 index 00000000..1af1134b --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxItems.java @@ -0,0 +1,43 @@ +package org.openapitools.openapidiff.core.model.schema; + +import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.DiffContext; +import org.openapitools.openapidiff.core.model.DiffResult; + +public class ChangedMaxItems implements Changed { + private final Integer oldValue; + private final Integer newValue; + private final DiffContext context; + + public ChangedMaxItems(Integer oldValue, Integer newValue, DiffContext context) { + this.oldValue = oldValue; + this.newValue = newValue; + this.context = context; + } + + @Override + public DiffResult isChanged() { + if (oldValue == null && newValue == null) { + return DiffResult.NO_CHANGES; + } + if (oldValue == null || newValue == null) { + return DiffResult.COMPATIBLE; + } + if (newValue < oldValue) { + return DiffResult.INCOMPATIBLE; + } + return DiffResult.COMPATIBLE; + } + + public Integer getOldValue() { + return oldValue; + } + + public Integer getNewValue() { + return newValue; + } + + public DiffContext getContext() { + return context; + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinItems.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinItems.java new file mode 100644 index 00000000..7791893c --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinItems.java @@ -0,0 +1,43 @@ +package org.openapitools.openapidiff.core.model.schema; + +import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.DiffContext; +import org.openapitools.openapidiff.core.model.DiffResult; + +public class ChangedMinItems implements Changed { + private final Integer oldValue; + private final Integer newValue; + private final DiffContext context; + + public ChangedMinItems(Integer oldValue, Integer newValue, DiffContext context) { + this.oldValue = oldValue; + this.newValue = newValue; + this.context = context; + } + + @Override + public DiffResult isChanged() { + if (oldValue == null && newValue == null) { + return DiffResult.NO_CHANGES; + } + if (oldValue == null || newValue == null) { + return DiffResult.COMPATIBLE; + } + if (newValue > oldValue) { + return DiffResult.INCOMPATIBLE; + } + return DiffResult.COMPATIBLE; + } + + public Integer getOldValue() { + return oldValue; + } + + public Integer getNewValue() { + return newValue; + } + + public DiffContext getContext() { + return context; + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/SchemaDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/SchemaDiffTest.java index 0b4eab6c..0ce22b93 100644 --- a/core/src/test/java/org/openapitools/openapidiff/core/SchemaDiffTest.java +++ b/core/src/test/java/org/openapitools/openapidiff/core/SchemaDiffTest.java @@ -134,4 +134,39 @@ public void changeMultipleOfHandling() { assertThat(props.get("field4").getMultipleOf().getLeft()).isEqualTo(BigDecimal.valueOf(10)); assertThat(props.get("field4").getMultipleOf().getRight()).isNull(); } + + @Test // issues #480 + public void changeMinMaxItemsHandling() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations( + "schemaDiff/schema-min-max-items-diff-1.yaml", + "schemaDiff/schema-min-max-items-diff-2.yaml"); + ChangedSchema changedSchema = + getRequestBodyChangedSchema( + changedOpenApi, POST, "/schema/array/items", "application/json"); + + assertThat(changedSchema).isNotNull(); + Map props = changedSchema.getChangedProperties(); + assertThat(props).isNotEmpty(); + + // Check increasing of minItems + assertThat(props.get("field1").getMinItems().isIncompatible()).isTrue(); + assertThat(props.get("field1").getMinItems().getOldValue()).isEqualTo(1); + assertThat(props.get("field1").getMinItems().getNewValue()).isEqualTo(2); + + // Check decreasing of minItems + assertThat(props.get("field2").getMinItems().isCompatible()).isTrue(); + assertThat(props.get("field2").getMinItems().getOldValue()).isEqualTo(20); + assertThat(props.get("field2").getMinItems().getNewValue()).isEqualTo(10); + + // Check increasing of maxItems + assertThat(props.get("field3").getMaxItems().isCompatible()).isTrue(); + assertThat(props.get("field3").getMaxItems().getOldValue()).isEqualTo(90); + assertThat(props.get("field3").getMaxItems().getNewValue()).isEqualTo(100); + + // Check decreasing of maxItems + assertThat(props.get("field4").getMaxItems().isIncompatible()).isTrue(); + assertThat(props.get("field4").getMaxItems().getOldValue()).isEqualTo(100); + assertThat(props.get("field4").getMaxItems().getNewValue()).isEqualTo(90); + } } diff --git a/core/src/test/resources/schemaDiff/schema-min-max-items-diff-1.yaml b/core/src/test/resources/schemaDiff/schema-min-max-items-diff-1.yaml new file mode 100644 index 00000000..23d84148 --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-min-max-items-diff-1.yaml @@ -0,0 +1,42 @@ +openapi: 3.1.0 +info: + description: Schema diff + title: schema diff + version: 1.0.0 +paths: + /schema/array/items: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TestDTO' +components: + schemas: + TestDTO: + type: object + properties: + field1: + type: array + items: + type: string + minItems: 1 + maxItems: 100 + field2: + type: array + items: + type: string + minItems: 20 + maxItems: 100 + field3: + type: array + items: + type: string + minItems: 1 + maxItems: 90 + field4: + type: array + items: + type: string + minItems: 1 + maxItems: 100 diff --git a/core/src/test/resources/schemaDiff/schema-min-max-items-diff-2.yaml b/core/src/test/resources/schemaDiff/schema-min-max-items-diff-2.yaml new file mode 100644 index 00000000..0d8e6199 --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-min-max-items-diff-2.yaml @@ -0,0 +1,42 @@ +openapi: 3.1.0 +info: + description: Schema diff + title: schema diff + version: 1.0.0 +paths: + /schema/array/items: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TestDTO' +components: + schemas: + TestDTO: + type: object + properties: + field1: + type: array + items: + type: string + minItems: 2 + maxItems: 100 + field2: + type: array + items: + type: string + minItems: 10 + maxItems: 100 + field3: + type: array + items: + type: string + minItems: 1 + maxItems: 100 + field4: + type: array + items: + type: string + minItems: 1 + maxItems: 90 From ea85b2be6c652828383b4980395ad63b5a093738 Mon Sep 17 00:00:00 2001 From: Aliaksandr Pinchuk Date: Thu, 27 Feb 2025 23:41:47 +0100 Subject: [PATCH 2/2] Fix imports --- .../openapitools/openapidiff/core/model/ChangedSchema.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 1c1cfc53..6c95799b 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 @@ -9,14 +9,14 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.openapitools.openapidiff.core.model.schema.ChangedEnum; +import org.openapitools.openapidiff.core.model.schema.ChangedMaxItems; import org.openapitools.openapidiff.core.model.schema.ChangedMaxLength; +import org.openapitools.openapidiff.core.model.schema.ChangedMinItems; import org.openapitools.openapidiff.core.model.schema.ChangedMultipleOf; import org.openapitools.openapidiff.core.model.schema.ChangedNumericRange; import org.openapitools.openapidiff.core.model.schema.ChangedReadOnly; import org.openapitools.openapidiff.core.model.schema.ChangedRequired; import org.openapitools.openapidiff.core.model.schema.ChangedWriteOnly; -import org.openapitools.openapidiff.core.model.schema.ChangedMaxItems; -import org.openapitools.openapidiff.core.model.schema.ChangedMinItems; public class ChangedSchema implements ComposedChanged { protected DiffContext context;