55import java .util .Collection ;
66import java .util .Collections ;
77import java .util .HashMap ;
8+ import java .util .LinkedHashMap ;
89import java .util .Map ;
10+ import java .util .Map .Entry ;
911import java .util .Optional ;
1012
1113import 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}
0 commit comments