Repository: swagger-api/swagger-parser
Pull request #928 (2018-11-14 12:35:31)

Fix for issue 911
Hi team,

This PR is to solve the issue #911 (Not able to identify the missed path variables).

Please review the PR and merge the same.

Thanks,
Mohammed Rizwan


Patch:
diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java
index d2660408..aafe64ad 100644
--- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java
+++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java
@@ -529,6 +529,22 @@ public class OpenAPIDeserializer {
                     }
                     ObjectNode path = (ObjectNode) pathValue;
                     PathItem pathObj = getPathItem(path,String.format("%s.'%s'", location,pathName), result);
+                    String[] eachPart = pathName.split("/");
+                    Arrays.stream(eachPart)
+                            .filter(part -> part.startsWith("{") && part.endsWith("}") && part.length() > 2)
+                            .forEach(part -> {
+                                String pathParam = part.substring(1, part.length() - 1);
+                                boolean definedInPathLevel = isPathParamDefined(pathParam, pathObj.getParameters());
+                                if (!definedInPathLevel) {
+                                    List<Operation> operationsInAPath = getAllOperationsInAPath(pathObj);
+                                    operationsInAPath.forEach(operation -> {
+                                        if (!isPathParamDefined(pathParam, operation.getParameters())) {
+                                            result.warning(location + ".'" + pathName + "'"," Declared path parameter " + pathParam + " needs to be defined as a path parameter in path or operation level");
+                                            return;
+                                        }
+                                    });
+                                }
+                            });
                     paths.put(pathName, pathObj);
                 }
             }
@@ -536,6 +552,41 @@ public class OpenAPIDeserializer {
         return paths;
     }
 
+    private boolean isPathParamDefined(String pathParam, List<Parameter> parameters) {
+        if (parameters == null || parameters.isEmpty()) {
+            return false;
+        } else {
+            Parameter pathParamDefined = parameters.stream()
+                            .filter(parameter -> pathParam.equals(parameter.getName()) && "path".equals(parameter.getIn()))
+                            .findFirst()
+                            .orElse(null);
+            if (pathParamDefined == null) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void addToOperationsList(List<Operation> operationsList, Operation operation) {
+        if (operation == null) {
+            return;
+        }
+        operationsList.add(operation);
+    }
+
+    public List<Operation> getAllOperationsInAPath(PathItem pathObj) {
+        List<Operation> operations = new ArrayList<>();
+        addToOperationsList(operations, pathObj.getGet());
+        addToOperationsList(operations, pathObj.getPut());
+        addToOperationsList(operations, pathObj.getPost());
+        addToOperationsList(operations, pathObj.getPatch());
+        addToOperationsList(operations, pathObj.getDelete());
+        addToOperationsList(operations, pathObj.getTrace());
+        addToOperationsList(operations, pathObj.getOptions());
+        addToOperationsList(operations, pathObj.getHead());
+        return operations;
+    }
+
     public PathItem getPathItem(ObjectNode obj, String location, ParseResult result) {
 
 


Test patch:
diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OAIDeserializationTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OAIDeserializationTest.java
index eb048f15..d0a0eba5 100644
--- a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OAIDeserializationTest.java
+++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OAIDeserializationTest.java
@@ -7,6 +7,7 @@ import io.swagger.v3.parser.OpenAPIV3Parser;
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertNotNull;
+import static org.testng.AssertJUnit.assertEquals;
 
 public class OAIDeserializationTest {
     @Test
@@ -34,4 +35,11 @@ public class OAIDeserializationTest {
 
         assertNotNull(result.getOpenAPI());
     }
+
+    @Test
+    public void testIssue911() {
+        SwaggerParseResult result = new OpenAPIV3Parser().readLocation("issue_911.yaml", null, null);
+        assertEquals(result.getMessages().size(),1);
+        assertNotNull(result.getOpenAPI());
+    }
 }
diff --git a/modules/swagger-parser-v3/src/test/resources/issue_911.yaml b/modules/swagger-parser-v3/src/test/resources/issue_911.yaml
new file mode 100644
index 00000000..cca5e416
--- /dev/null
+++ b/modules/swagger-parser-v3/src/test/resources/issue_911.yaml
@@ -0,0 +1,48 @@
+openapi: 3.0.0
+servers:
+- url: 'http://localhost:8000/v2/api'
+info:
+  description: >-
+    This is a sample server Petstore server.  You can find out more about
+    Swagger at http://swagger.io or on
+    irc.freenode.net, #swagger.  For this sample, you can use the api key
+    "special-key" to test the authorization filters
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    name: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+externalDocs:
+  description: Find more info here
+  url: 'https://swagger.io'
+tags:
+- name: pet
+  description: Pet Operations
+  externalDocs:
+    url: 'http://swagger.io'
+- name: user
+  description: All about the Users
+paths:
+  '/pet/{petId}/{pathParamNotDefined}':
+    get:
+      tags:
+      - pet
+      summary: Find pet by ID
+      description: >-
+        Returns a pet when ID < 10.  ID > 10 or nonintegers will simulate API
+        error conditions
+      operationId: getPetById
+      responses:
+        200:
+          description: 200 ok
+      parameters:
+      - name: petId
+        in: path
+        description: ID of pet that needs to be fetched
+        required: true
+        schema:
+          type: integer
+          format: int64
\ No newline at end of file
diff --git a/modules/swagger-parser/src/test/resources/duplicateOperationId.json b/modules/swagger-parser/src/test/resources/duplicateOperationId.json
index 69152c53..1b208503 100644
--- a/modules/swagger-parser/src/test/resources/duplicateOperationId.json
+++ b/modules/swagger-parser/src/test/resources/duplicateOperationId.json
@@ -6,6 +6,15 @@
   },
   "paths": {
     "/pets/{id}": {
+      "parameters" : [
+        {
+          "in": "path",
+          "name": "id",
+          "schema": {
+            "type": "string"
+          }
+        }
+      ],
       "get": {
         "operationId": "getPetsById",
         "responses": {