Skip to content

Commit 83369bb

Browse files
author
Chris Wiechmann
committed
Adding support for full API exposure path
Fixes #2
1 parent e468377 commit 83369bb

File tree

2 files changed

+88
-3
lines changed

2 files changed

+88
-3
lines changed

src/main/java/com/axway/apim/openapi/validator/OpenAPIValidator.java

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
import java.util.Collection;
66
import java.util.Collections;
77
import java.util.HashMap;
8+
import java.util.LinkedHashMap;
89
import java.util.Map;
10+
import java.util.Map.Entry;
911
import java.util.Optional;
1012

1113
import com.atlassian.oai.validator.OpenApiInteractionValidator;
@@ -29,6 +31,8 @@ public class OpenAPIValidator
2931

3032
private OpenApiInteractionValidator validator;
3133

34+
private MaxSizeHashMap<String, String> exposurePath2SpecifiedPathMap = new MaxSizeHashMap<String, String>();
35+
3236
private int payloadLogMaxLength = 40;
3337

3438
public static synchronized OpenAPIValidator getInstance(String openAPISpec) {
@@ -63,6 +67,7 @@ public static synchronized OpenAPIValidator getInstance(String apiId, String use
6367
private OpenAPIValidator(String openAPISpec) {
6468
super();
6569
try {
70+
exposurePath2SpecifiedPathMap.setMaxSize(1000);
6671
// Check if openAPISpec is a valid URL
6772
new URI(openAPISpec);
6873
// If it does not fail, load it from the URL
@@ -89,7 +94,41 @@ private OpenAPIValidator(String apiId, String username, String password, String
8994

9095
public boolean isValidRequest(String payload, String verb, String path, QueryStringHeaderSet queryParams, HeaderSet headers) {
9196
Utils.traceMessage("Validate request: [verb: "+verb+", path: '"+path+"', payload: '"+Utils.getContentStart(payload, payloadLogMaxLength, true)+"']", TraceLevel.INFO);
92-
ValidationReport validationReport = validateRequest(payload, verb, path, queryParams, headers);
97+
ValidationReport validationReport = null;
98+
String originalPath = path;
99+
// The given path might be the FE-API exposed path which is not always part of the API-Specification.
100+
// If for example the Petstore is exposed with /petstore/v3 the path we get might be /petstore/v3/store/order/78787
101+
// and with that, the validation fails, as the specification doesn't contain this path.
102+
// The following code nevertheless tries to find the matching API path by removing the first part of the path and
103+
// repeating the process max. 5 times.
104+
105+
boolean cachePath = false;
106+
// Perhaps the path has already looked up and matched to the specified path (e.g. /petstore/v3/store/order/78787 --> /store/order/{orderId}
107+
if(exposurePath2SpecifiedPathMap.containsKey(path)) {
108+
// In that case perform the validation based on the cached path
109+
validationReport = validateRequest(payload, verb, exposurePath2SpecifiedPathMap.get(path), queryParams, headers);
110+
} else {
111+
// Otherwise try to find the belonging specified path
112+
for(int i=0; i<5;i++) {
113+
validationReport = validateRequest(payload, verb, path, queryParams, headers);
114+
if(validationReport.hasErrors()) {
115+
if(validationReport.getMessages().toString().contains("No API path found that matches request")) {
116+
// Only cache the path, if a direct hit fails
117+
cachePath = true;
118+
// Remove the first path (e.g. /petstore) from the path repeat the process
119+
path = path.substring(path.indexOf("/", 1), path.length());
120+
} else {
121+
break;
122+
}
123+
} else {
124+
break;
125+
}
126+
}
127+
if(cachePath) {
128+
exposurePath2SpecifiedPathMap.put(originalPath, path);
129+
}
130+
}
131+
93132
if (validationReport.hasErrors()) {
94133
return false;
95134
} else {
@@ -192,4 +231,24 @@ public int getPayloadLogMaxLength() {
192231
public void setPayloadLogMaxLength(int payloadLogMaxLength) {
193232
this.payloadLogMaxLength = payloadLogMaxLength;
194233
}
234+
235+
static class MaxSizeHashMap<K, V> extends LinkedHashMap<K, V> {
236+
237+
private static final long serialVersionUID = 1L;
238+
239+
private int maxSize;
240+
241+
@Override
242+
protected boolean removeEldestEntry(Entry<K, V> eldest) {
243+
return size() > maxSize;
244+
}
245+
246+
public void setMaxSize(int maxSize) {
247+
this.maxSize = maxSize;
248+
}
249+
}
250+
251+
public MaxSizeHashMap<String, String> getExposurePath2SpecifiedPathMap() {
252+
return exposurePath2SpecifiedPathMap;
253+
}
195254
}

src/test/java/com/axway/apim/openapi/validator/TestOpenAPIValidator.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,43 @@ public void validGetRequestPetsByStatusPetstoreSwagger20() throws IOException
8787
}
8888

8989
@Test
90-
public void validDeleteRequestOrderBasedOnId() throws IOException
90+
public void validRequestWithFullPathAndPathParameter() throws IOException
9191
{
9292
String swagger = Files.readFile(this.getClass().getClassLoader().getResourceAsStream(TEST_PACKAGE + "PetstoreSwagger2.0.json"));
9393
OpenAPIValidator validator = OpenAPIValidator.getInstance(swagger);
9494

95-
String path = "/store/order/1234";
95+
validator.getExposurePath2SpecifiedPathMap().setMaxSize(2);
96+
97+
String path = "/api/petstore/v3/store/order/1234";
9698
String verb = "DELETE";
9799
HeaderSet headers = new HeaderSet();
98100
headers.addHeader("Content-Type", "application/json");
99101

100102
Assert.assertTrue(validator.isValidRequest(null, verb, path, null, headers), "Request should be valid!");
103+
// Run it a second time, which should use the cached path
104+
Assert.assertTrue(validator.isValidRequest(null, verb, path, null, headers), "Request should be valid!");
105+
106+
path = "/api/petstore/v3/store/order/5345";
107+
Assert.assertTrue(validator.isValidRequest(null, verb, path, null, headers), "Request should be valid!");
108+
109+
path = "/api/petstore/v3/store/order/434312";
110+
Assert.assertTrue(validator.isValidRequest(null, verb, path, null, headers), "Request should be valid!");
111+
112+
Assert.assertEquals(validator.getExposurePath2SpecifiedPathMap().size(), 2, "Cached paths should be two as the size is limited");
113+
}
114+
115+
@Test
116+
public void invalidRequestWithFullPathAndPathParameter() throws IOException
117+
{
118+
String swagger = Files.readFile(this.getClass().getClassLoader().getResourceAsStream(TEST_PACKAGE + "PetstoreSwagger2.0.json"));
119+
OpenAPIValidator validator = OpenAPIValidator.getInstance(swagger);
120+
121+
String path = "/api/petstore/v3/store/order/invalidPatameter";
122+
String verb = "DELETE";
123+
HeaderSet headers = new HeaderSet();
124+
headers.addHeader("Content-Type", "application/json");
125+
126+
Assert.assertFalse(validator.isValidRequest(null, verb, path, null, headers));
101127
}
102128

103129
@Test

0 commit comments

Comments
 (0)