Skip to content

Commit bd63232

Browse files
committed
Add possibility to override API path matching strategy
Fixes #823
1 parent 779a012 commit bd63232

File tree

2 files changed

+35
-99
lines changed

2 files changed

+35
-99
lines changed

core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiffOptions.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
import org.apache.commons.configuration2.CompositeConfiguration;
77
import org.apache.commons.configuration2.YAMLConfiguration;
88
import org.apache.commons.configuration2.ex.ConfigurationException;
9+
import org.openapitools.openapidiff.core.compare.matchers.DefaultPathMatcher;
10+
import org.openapitools.openapidiff.core.compare.matchers.PathMatcher;
911

1012
public class OpenApiDiffOptions {
1113
private final CompositeConfiguration config;
14+
private PathMatcher pathMatcher;
1215

1316
private OpenApiDiffOptions(CompositeConfiguration config) {
1417
this.config = config;
@@ -18,6 +21,10 @@ public CompositeConfiguration getConfig() {
1821
return config;
1922
}
2023

24+
public PathMatcher getPathMatcher() {
25+
return pathMatcher != null ? pathMatcher : new DefaultPathMatcher();
26+
}
27+
2128
public static Builder builder() {
2229
return new Builder();
2330
}
@@ -45,5 +52,10 @@ public Builder configProperty(String propKey, String propVal) {
4552
public OpenApiDiffOptions build() {
4653
return built;
4754
}
55+
56+
public Builder pathMatcher(PathMatcher matcher) {
57+
built.pathMatcher = matcher;
58+
return this;
59+
}
4860
}
4961
}
Lines changed: 23 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
package org.openapitools.openapidiff.core.compare;
22

3-
import io.swagger.v3.oas.models.Operation;
43
import io.swagger.v3.oas.models.PathItem;
54
import io.swagger.v3.oas.models.Paths;
6-
import io.swagger.v3.oas.models.parameters.Parameter;
75
import java.util.*;
86
import java.util.regex.Matcher;
97
import java.util.regex.Pattern;
10-
import java.util.stream.IntStream;
118
import org.openapitools.openapidiff.core.model.Changed;
129
import org.openapitools.openapidiff.core.model.ChangedPaths;
1310
import org.openapitools.openapidiff.core.model.DiffContext;
@@ -22,135 +19,62 @@ public PathsDiff(OpenApiDiff openApiDiff) {
2219
this.openApiDiff = openApiDiff;
2320
}
2421

25-
private static String normalizePath(String path) {
26-
return path.replaceAll(REGEX_PATH, "{}");
27-
}
28-
29-
private static List<String> extractParameters(String path) {
30-
ArrayList<String> params = new ArrayList<>();
31-
Pattern pattern = Pattern.compile(REGEX_PATH);
32-
Matcher matcher = pattern.matcher(path);
33-
while (matcher.find()) {
34-
params.add(matcher.group(1));
35-
}
36-
return params;
37-
}
38-
3922
public DeferredChanged<ChangedPaths> diff(
4023
final Map<String, PathItem> left, final Map<String, PathItem> right) {
4124
DeferredBuilder<Changed> builder = new DeferredBuilder<>();
4225

4326
ChangedPaths changedPaths = new ChangedPaths(left, right, openApiDiff.getOptions());
4427
changedPaths.getIncreased().putAll(right);
4528

46-
left.keySet()
29+
left.entrySet()
4730
.forEach(
48-
(String url) -> {
49-
PathItem leftPath = left.get(url);
50-
String template = normalizePath(url);
31+
pathEntry -> {
32+
String leftUrl = pathEntry.getKey();
33+
PathItem leftPath = pathEntry.getValue();
5134
Optional<Map.Entry<String, PathItem>> result =
52-
changedPaths.getIncreased().entrySet().stream()
53-
.filter(item -> normalizePath(item.getKey()).equals(template))
54-
.min(
55-
(a, b) -> {
56-
if (methodsAndParametersIntersect(a.getValue(), b.getValue())) {
57-
throw new IllegalArgumentException(
58-
"Two path items have the same signature: " + template);
59-
}
60-
if (a.getKey().equals(url)) {
61-
return -1;
62-
} else if (b.getKey().equals((url))) {
63-
return 1;
64-
} else {
65-
HashSet<PathItem.HttpMethod> methodsA =
66-
new HashSet<>(a.getValue().readOperationsMap().keySet());
67-
methodsA.retainAll(leftPath.readOperationsMap().keySet());
68-
HashSet<PathItem.HttpMethod> methodsB =
69-
new HashSet<>(b.getValue().readOperationsMap().keySet());
70-
methodsB.retainAll(leftPath.readOperationsMap().keySet());
71-
return Integer.compare(methodsB.size(), methodsA.size());
72-
}
73-
});
35+
openApiDiff
36+
.getOptions()
37+
.getPathMatcher()
38+
.find(pathEntry, changedPaths.getIncreased());
7439
if (result.isPresent()) {
7540
String rightUrl = result.get().getKey();
7641
PathItem rightPath = changedPaths.getIncreased().remove(rightUrl);
7742
Map<String, String> params = new LinkedHashMap<>();
78-
if (!url.equals(rightUrl)) {
79-
List<String> oldParams = extractParameters(url);
43+
if (!leftUrl.equals(rightUrl)) {
44+
List<String> oldParams = extractParameters(leftUrl);
8045
List<String> newParams = extractParameters(rightUrl);
8146
for (int i = 0; i < oldParams.size(); i++) {
8247
params.put(oldParams.get(i), newParams.get(i));
8348
}
8449
}
8550
DiffContext context = new DiffContext(openApiDiff.getOptions());
86-
context.setUrl(url);
51+
context.setUrl(leftUrl);
8752
context.setParameters(params);
88-
context.setLeftAndRightUrls(url, rightUrl);
53+
context.setLeftAndRightUrls(leftUrl, rightUrl);
8954
builder
9055
.with(openApiDiff.getPathDiff().diff(leftPath, rightPath, context))
9156
.ifPresent(path -> changedPaths.getChanged().put(rightUrl, path));
9257
} else {
93-
changedPaths.getMissing().put(url, leftPath);
58+
changedPaths.getMissing().put(leftUrl, leftPath);
9459
}
9560
});
9661
return builder.buildIsChanged(changedPaths);
9762
}
9863

64+
private List<String> extractParameters(String path) {
65+
ArrayList<String> params = new ArrayList<>();
66+
Pattern pattern = Pattern.compile(REGEX_PATH);
67+
Matcher matcher = pattern.matcher(path);
68+
while (matcher.find()) {
69+
params.add(matcher.group(1));
70+
}
71+
return params;
72+
}
73+
9974
public static Paths valOrEmpty(Paths path) {
10075
if (path == null) {
10176
path = new Paths();
10277
}
10378
return path;
10479
}
105-
106-
/**
107-
* @param a a path form the open api spec
108-
* @param b another path from the same open api spec
109-
* @return <code>true</code> in case both paths are of the same method AND their templated
110-
* parameters are of the same type; <code>false</code> otherwise
111-
*/
112-
private static boolean methodsAndParametersIntersect(PathItem a, PathItem b) {
113-
Set<PathItem.HttpMethod> methodsA = a.readOperationsMap().keySet();
114-
for (PathItem.HttpMethod method : b.readOperationsMap().keySet()) {
115-
if (methodsA.contains(method)) {
116-
Operation left = a.readOperationsMap().get(method);
117-
Operation right = b.readOperationsMap().get(method);
118-
if (left.getParameters().size() == right.getParameters().size()) {
119-
return parametersIntersect(left.getParameters(), right.getParameters());
120-
}
121-
return false;
122-
}
123-
}
124-
return false;
125-
}
126-
127-
/**
128-
* Checks if provided parameter pairs are equal by type and format
129-
*
130-
* @param left parameters from the first compared method
131-
* @param right parameters from the second compared method
132-
* @return <code>true</code> in case each parameter pair is of the same type; <code>false</code>
133-
* otherwise
134-
*/
135-
private static boolean parametersIntersect(List<Parameter> left, List<Parameter> right) {
136-
int parametersSize = left.size();
137-
long intersectedParameters =
138-
IntStream.range(0, left.size())
139-
.filter(i -> parametersTypeEquals(left.get(i), right.get(i)))
140-
.count();
141-
return parametersSize == intersectedParameters;
142-
}
143-
144-
/**
145-
* Checks if provided parameter pair is equal by type and format
146-
*
147-
* @param left parameter from the first compared method
148-
* @param right parameter from the second compared method
149-
* @return <code>true</code> in case parameter pair is of the same type; <code>false</code>
150-
* otherwise
151-
*/
152-
private static boolean parametersTypeEquals(Parameter left, Parameter right) {
153-
return Objects.equals(left.getSchema().getType(), right.getSchema().getType())
154-
&& Objects.equals(left.getSchema().getFormat(), right.getSchema().getFormat());
155-
}
15680
}

0 commit comments

Comments
 (0)