Skip to content

Commit 258c7d4

Browse files
committed
Vendor extensions support
1 parent 298cd46 commit 258c7d4

File tree

7 files changed

+120
-2
lines changed

7 files changed

+120
-2
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ try {
129129
}
130130
```
131131

132+
### Extensions
133+
This project uses Java Service Provider Inteface (SPI) so additional extensions can be added.
134+
135+
To build your own extension, you simply need to create a `src/main/resources/META-INF/services/com.qdesrame.openapi.diff.compare.ExtensionDiff` file with the full classname of your implementation. Your class must also implement the `com.qdesrame.openapi.diff.compare.ExtensionDiff` interface. Then, including your library with the `openapi-diff` module will cause it to be triggered automatically.
136+
132137
# Example
133138
### CLI Output
134139

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.qdesrame.openapi.diff.compare;
2+
3+
import com.qdesrame.openapi.diff.model.Changed;
4+
import com.qdesrame.openapi.diff.model.DiffContext;
5+
6+
import java.util.Optional;
7+
8+
public interface ExtensionDiff {
9+
10+
ExtensionDiff setOpenApiDiff(OpenApiDiff openApiDiff);
11+
12+
String getName();
13+
14+
Optional<Changed> diff(Object left, Object right, DiffContext context);
15+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.qdesrame.openapi.diff.compare;
2+
3+
import com.qdesrame.openapi.diff.model.DiffContext;
4+
import com.qdesrame.openapi.diff.model.schema.ChangedExtensions;
5+
6+
import java.util.*;
7+
8+
import static com.qdesrame.openapi.diff.utils.ChangedUtils.isChanged;
9+
10+
public class ExtensionsDiff {
11+
private final OpenApiDiff openApiDiff;
12+
13+
private ServiceLoader<ExtensionDiff> extensionsLoader = ServiceLoader.load(ExtensionDiff.class);
14+
private List<ExtensionDiff> extensionsDiff = new ArrayList<>();
15+
16+
public ExtensionsDiff(OpenApiDiff openApiDiff) {
17+
this.openApiDiff = openApiDiff;
18+
this.extensionsLoader.reload();
19+
for (ExtensionDiff anExtensionsLoader : this.extensionsLoader) {
20+
extensionsDiff.add(anExtensionsLoader);
21+
}
22+
}
23+
24+
public Optional<ChangedExtensions> diff(Map<String, Object> left, Map<String, Object> right, DiffContext context) {
25+
if (null == left) left = new HashMap<>();
26+
if (null == right) right = new HashMap<>();
27+
ChangedExtensions changedExtensions = new ChangedExtensions(left, new HashMap<>(right), context);
28+
changedExtensions.getIncreased().putAll(right);
29+
for (String key : left.keySet()) {
30+
if (changedExtensions.getIncreased().containsKey(key)) {
31+
Optional<ExtensionDiff> extensionDiff = extensionsDiff.stream()
32+
.filter(diff -> ("x-" + diff.getName()).equals(key)).findFirst();
33+
Object leftValue = left.get(key);
34+
Object rightValue = changedExtensions.getIncreased().remove(key);
35+
extensionDiff.ifPresent(diff -> diff.setOpenApiDiff(openApiDiff).diff(leftValue, rightValue, context)
36+
.ifPresent(changed -> changedExtensions.getChanged().put(key, changed)));
37+
} else {
38+
changedExtensions.getMissing().put(key, left.get(key));
39+
}
40+
}
41+
return isChanged(changedExtensions);
42+
}
43+
}

src/main/java/com/qdesrame/openapi/diff/compare/OpenApiDiff.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public class OpenApiDiff {
4040
private SecuritySchemeDiff securitySchemeDiff;
4141
private OAuthFlowsDiff oAuthFlowsDiff;
4242
private OAuthFlowDiff oAuthFlowDiff;
43+
private ExtensionsDiff extensionsDiff;
4344

4445
private OpenAPI oldSpecOpenApi;
4546
private OpenAPI newSpecOpenApi;
@@ -84,6 +85,7 @@ private void initializeFields() {
8485
this.securitySchemeDiff = new SecuritySchemeDiff(this);
8586
this.oAuthFlowsDiff = new OAuthFlowsDiff(this);
8687
this.oAuthFlowDiff = new OAuthFlowDiff(this);
88+
this.extensionsDiff = new ExtensionsDiff(this);
8789
}
8890

8991
private ChangedOpenApi compare() {
@@ -201,6 +203,10 @@ public OAuthFlowDiff getoAuthFlowDiff() {
201203
return oAuthFlowDiff;
202204
}
203205

206+
public ExtensionsDiff getExtensionsDiff() {
207+
return extensionsDiff;
208+
}
209+
204210
public OpenAPI getOldSpecOpenApi() {
205211
return oldSpecOpenApi;
206212
}

src/main/java/com/qdesrame/openapi/diff/compare/schemadiffresult/SchemaDiffResult.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.qdesrame.openapi.diff.model.ChangedSchema;
66
import com.qdesrame.openapi.diff.model.DiffContext;
77
import com.qdesrame.openapi.diff.model.ListDiff;
8+
import com.qdesrame.openapi.diff.model.schema.ChangedExtensions;
89
import com.qdesrame.openapi.diff.model.schema.ChangedReadOnly;
910
import com.qdesrame.openapi.diff.model.schema.ChangedWriteOnly;
1011
import io.swagger.v3.oas.models.Components;
@@ -47,6 +48,8 @@ public Optional<ChangedSchema> diff(HashSet<String> refSet, Components leftCompo
4748
changedSchema.setChangedReadOnly(new ChangedReadOnly(context, left.getReadOnly(), right.getReadOnly()));
4849
changedSchema.setChangedWriteOnly(new ChangedWriteOnly(context, left.getWriteOnly(), right.getWriteOnly()));
4950
changedSchema.setChangedMaxLength(!Objects.equals(left.getMaxLength(), right.getMaxLength()));
51+
Optional<ChangedExtensions> changedExtensions = openApiDiff.getExtensionsDiff().diff(left.getExtensions(), right.getExtensions(), context);
52+
changedExtensions.ifPresent(changedSchema::setChangedExtensions);
5053

5154
Map<String, Schema> leftProperties = null == left ? null : left.getProperties();
5255
Map<String, Schema> rightProperties = null == right ? null : right.getProperties();

src/main/java/com/qdesrame/openapi/diff/model/ChangedSchema.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.qdesrame.openapi.diff.model;
22

3+
import com.qdesrame.openapi.diff.model.schema.ChangedExtensions;
34
import com.qdesrame.openapi.diff.model.schema.ChangedReadOnly;
45
import com.qdesrame.openapi.diff.model.schema.ChangedWriteOnly;
56
import com.qdesrame.openapi.diff.utils.ChangedUtils;
@@ -38,6 +39,7 @@ public class ChangedSchema implements Changed {
3839
protected boolean discriminatorPropertyChanged;
3940
protected ChangedOneOfSchema changedOneOfSchema;
4041
protected ChangedSchema addPropChangedSchema;
42+
protected ChangedExtensions changedExtensions;
4143

4244
public ChangedSchema() {
4345
increasedProperties = new HashMap<>();
@@ -53,7 +55,8 @@ public DiffResult isChanged() {
5355
&& !changeFormat && increasedProperties.size() == 0 && missingProperties.size() == 0
5456
&& changedProperties.values().size() == 0 && !changeDeprecated
5557
&& (changeRequired == null || changeRequired.isUnchanged()) && !discriminatorPropertyChanged
56-
&& ChangedUtils.isUnchanged(addPropChangedSchema) && ChangedUtils.isUnchanged(changedOneOfSchema)) {
58+
&& ChangedUtils.isUnchanged(addPropChangedSchema) && ChangedUtils.isUnchanged(changedOneOfSchema)
59+
&& ChangedUtils.isUnchanged(changedExtensions)) {
5760
return DiffResult.NO_CHANGES;
5861
}
5962
boolean backwardCompatibleForRequest = (changeEnum == null || changeEnum.getMissing().isEmpty()) &&
@@ -71,7 +74,8 @@ public DiffResult isChanged() {
7174
if ((context.isRequest() && backwardCompatibleForRequest || context.isResponse() && backwardCompatibleForResponse)
7275
&& !changedType && !discriminatorPropertyChanged && ChangedUtils.isCompatible(changedOneOfSchema)
7376
&& ChangedUtils.isCompatible(addPropChangedSchema)
74-
&& changedProperties.values().stream().allMatch(Changed::isCompatible)) {
77+
&& changedProperties.values().stream().allMatch(Changed::isCompatible)
78+
&& ChangedUtils.isCompatible(changedExtensions)) {
7579
return DiffResult.COMPATIBLE;
7680
}
7781

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.qdesrame.openapi.diff.model.schema;
2+
3+
import com.qdesrame.openapi.diff.model.Changed;
4+
import com.qdesrame.openapi.diff.model.DiffContext;
5+
import com.qdesrame.openapi.diff.model.DiffResult;
6+
import lombok.Getter;
7+
import lombok.Setter;
8+
9+
import java.util.LinkedHashMap;
10+
import java.util.Map;
11+
12+
@Getter
13+
@Setter
14+
public class ChangedExtensions implements Changed {
15+
private final Map<String, Object> oldExtensions;
16+
private final Map<String, Object> newExtensions;
17+
private final DiffContext context;
18+
19+
private Map<String, Object> increased;
20+
private Map<String, Object> missing;
21+
private Map<String, Changed> changed;
22+
23+
public ChangedExtensions(Map<String, Object> oldExtensions, Map<String, Object> newExtensions, DiffContext context) {
24+
this.oldExtensions = oldExtensions;
25+
this.newExtensions = newExtensions;
26+
this.context = context;
27+
this.increased = new LinkedHashMap<>();
28+
this.missing = new LinkedHashMap<>();
29+
this.changed = new LinkedHashMap<>();
30+
}
31+
32+
@Override
33+
public DiffResult isChanged() {
34+
if (increased.isEmpty() && missing.isEmpty() && changed.isEmpty()) {
35+
return DiffResult.NO_CHANGES;
36+
}
37+
if (changed.values().stream().allMatch(Changed::isCompatible)) {
38+
return DiffResult.COMPATIBLE;
39+
}
40+
return DiffResult.INCOMPATIBLE;
41+
}
42+
}

0 commit comments

Comments
 (0)