Repository: swagger-api/swagger-parser (java)

Issue:
error when parsing relative $ref
Hi,

I have an OpenAPI description with relative $refs that fails to parse with any version larger than io.swagger.parser.v3:swagger-parser:2.0.28.

It worked until  2.0.28.

See below for the OpenAPI yaml files and the exception.

I have created a test-case that throws the below exception.

```
broken-ref/apis/foo.yaml
broken-ref/examples/foo.example.yaml
broken-ref/models/foo.model.yaml
broken-ref/openapi30.yaml
 ```

```yaml
#broken-ref/apis/foo.yaml
openapi: 3.0.0
info:
  title: swagger parser bug, worked until 2.0.28 but not since 2.0.29
  version: '1.0'

paths:

  '/foo':
    post:
      summary: create foo
      description: Create a new foo.
      responses:
        '200':
          description: a Foo
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Foo'
              examples:
                Foo Bar:
                  $ref: '#/components/examples/FooExample'
      requestBody:
        description: foo data
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Foo'
            examples:
              Foo Bar:
                $ref: '#/components/examples/FooExample'

components:
  schemas:
    Foo:
      $ref: ../models/foo.model.yaml
  examples:
    FooExample:
      $ref: ../examples/foo.example.yaml

```

```yaml
# broken-ref/examples/foo.example.yaml
value:
  foo: bar
```

```yaml
#broken-ref/models/foo.model.yaml
title: Foo
type: object
properties:
  foo:
    type: string
```

```yaml
# broken-ref/openapi30.yaml
openapi: 3.0.3
info:
  title: ref chain
  version: '1'

paths:
  /foo:
    $ref: ./apis/foo.yaml#/paths/~1foo
```

```java
[main] WARN io.swagger.v3.parser.OpenAPIV3Parser - Exception while resolving:
java.lang.RuntimeException: Unable to load RELATIVE ref: foo.example path: /Users/hauner/Development/openapi/swagger-parser/modules/swagger-parser-v3/target/test-classes/broken-ref
	at io.swagger.v3.parser.util.RefUtils.readExternalRef(RefUtils.java:216)
	at io.swagger.v3.parser.ResolverCache.loadRef(ResolverCache.java:150)
	at io.swagger.v3.parser.processors.ExternalRefProcessor.processRefToExternalExample(ExternalRefProcessor.java:691)
	at io.swagger.v3.parser.processors.ExampleProcessor.processReferenceExample(ExampleProcessor.java:45)
	at io.swagger.v3.parser.processors.ExampleProcessor.processExample(ExampleProcessor.java:30)
	at io.swagger.v3.parser.processors.ComponentsProcessor.processExamples(ComponentsProcessor.java:183)
	at io.swagger.v3.parser.processors.ComponentsProcessor.processComponents(ComponentsProcessor.java:111)
	at io.swagger.v3.parser.OpenAPIResolver.resolve(OpenAPIResolver.java:73)
	at io.swagger.v3.parser.OpenAPIResolver.resolve(OpenAPIResolver.java:59)
	at io.swagger.v3.parser.OpenAPIV3Parser.resolve(OpenAPIV3Parser.java:226)
	at io.swagger.v3.parser.OpenAPIV3Parser.readContents(OpenAPIV3Parser.java:178)
	at io.swagger.v3.parser.OpenAPIV3Parser.readLocation(OpenAPIV3Parser.java:94)
	at io.swagger.v3.parser.test.BrokenRefTest.testBrokenRef(BrokenRefTest.java:18)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:86)
	at org.testng.internal.Invoker.invokeMethod(Invoker.java:643)
	at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:820)
	at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1128)
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:129)
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:112)
	at org.testng.TestRunner.privateRun(TestRunner.java:782)
	at org.testng.TestRunner.run(TestRunner.java:632)
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:366)
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:361)
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:319)
	at org.testng.SuiteRunner.run(SuiteRunner.java:268)
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1244)
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1169)
	at org.testng.TestNG.run(TestNG.java:1064)
	at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
	at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:109)
Caused by: java.lang.RuntimeException: Could not find foo.example on the classpath
	at io.swagger.v3.parser.util.ClasspathHelper.loadFileFromClasspath(ClasspathHelper.java:31)
	at io.swagger.v3.parser.util.RefUtils.readExternalRef(RefUtils.java:210)
	... 35 more
```

Pull request description:
fix for issue-1865 - dereferencing files with dots
fixes #1865 

This PR addresses issue 1865, which was failing due to reference files containing a dot other than the extension name, which is valid and should not fail. fix for issue-1865

Patch:
diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/RefUtils.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/RefUtils.java
index 7fcf56a4..561a3b56 100644
--- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/RefUtils.java
+++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/RefUtils.java
@@ -14,6 +14,7 @@ import java.io.InputStream;
 import java.net.URL;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Optional;
 
@@ -47,10 +48,13 @@ public class RefUtils {
             plausibleName = filePathElements[filePathElements.length - 1];
 
             final String[] split = plausibleName.split("\\.");
-            // Fix for issue-1621
-            plausibleName = split[0];
-            for (int i = 1; i < split.length - 1; i++) {
-                plausibleName += "." + split[i];
+            // Fix for issue-1621 and issue-1865
+            //validate number of dots
+            if(split.length > 2) {
+                //Remove dot so ref can be interpreted as internal and relative in Swagger-Core schema class 'set$ref'
+                plausibleName = String.join("", Arrays.copyOf(split, split.length - 1));
+            }else{
+                plausibleName = split[0];
             }
         }
 
@@ -225,4 +229,4 @@ public class RefUtils {
             return IOUtils.toString(inputStream, UTF_8);
         }
     }
-}
\ No newline at end of file
+}


Test patch:
diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java
index 5bb8fc81..97eac48a 100644
--- a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java
+++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java
@@ -86,6 +86,36 @@ public class OpenAPIV3ParserTest {
     protected int serverPort = getDynamicPort();
     protected WireMockServer wireMockServer;
 
+    @Test
+    public void testIssue1621() {
+        final ParseOptions parseOptions = new ParseOptions();
+        parseOptions.setResolve(true);
+        parseOptions.setResolveFully(true);
+        parseOptions.setResolveCombinators(false);
+        SwaggerParseResult parseResult = new OpenAPIV3Parser().readLocation("issue-1621/example.openapi.yaml", null, parseOptions);
+        assertEquals(0, parseResult.getMessages().size());
+        OpenAPI api = parseResult.getOpenAPI();
+        assertEquals( api.getPaths()
+                .get("/example")
+                .getPost()
+                .getRequestBody()
+                .getContent()
+                .get("application/json")
+                .getSchema()
+                .getTitle(),"POST Example");
+    }
+
+    @Test
+    public void testIssue1865() throws Exception {
+        ParseOptions options = new ParseOptions();
+        options.setResolve(true);
+        SwaggerParseResult result = new OpenAPIV3Parser().readLocation("src/test/resources/issue-1865/openapi30.yaml", null, options);
+
+        Assert.assertNotNull(result);
+        Assert.assertNotNull(result.getOpenAPI());
+        Assert.assertEquals(result.getOpenAPI().getComponents().getSchemas().get("Foo").get$ref(), "#/components/schemas/foomodel");
+    }
+
     @Test
     public void testIssue1777() {
         OpenAPIV3Parser openApiParser = new OpenAPIV3Parser();
diff --git a/modules/swagger-parser/src/test/resources/issue-1621/example.get.yaml b/modules/swagger-parser-v3/src/test/resources/issue-1621/example.get.yaml
similarity index 100%
rename from modules/swagger-parser/src/test/resources/issue-1621/example.get.yaml
rename to modules/swagger-parser-v3/src/test/resources/issue-1621/example.get.yaml
diff --git a/modules/swagger-parser/src/test/resources/issue-1621/example.openapi.yaml b/modules/swagger-parser-v3/src/test/resources/issue-1621/example.openapi.yaml
similarity index 100%
rename from modules/swagger-parser/src/test/resources/issue-1621/example.openapi.yaml
rename to modules/swagger-parser-v3/src/test/resources/issue-1621/example.openapi.yaml
diff --git a/modules/swagger-parser/src/test/resources/issue-1621/example.post.yaml b/modules/swagger-parser-v3/src/test/resources/issue-1621/example.post.yaml
similarity index 100%
rename from modules/swagger-parser/src/test/resources/issue-1621/example.post.yaml
rename to modules/swagger-parser-v3/src/test/resources/issue-1621/example.post.yaml
diff --git a/modules/swagger-parser-v3/src/test/resources/issue-1865/apis/foo.yaml b/modules/swagger-parser-v3/src/test/resources/issue-1865/apis/foo.yaml
new file mode 100644
index 00000000..2b0c3d42
--- /dev/null
+++ b/modules/swagger-parser-v3/src/test/resources/issue-1865/apis/foo.yaml
@@ -0,0 +1,24 @@
+#broken-ref/apis/foo.yaml
+openapi: 3.0.0
+info:
+  title: swagger parser bug, worked until 2.0.28 but not since 2.0.29
+  version: '1.0'
+
+paths:
+
+  '/foo':
+    post:
+      summary: create foo
+      description: Create a new foo.
+      responses:
+        '200':
+          description: a Foo
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Foo'
+
+components:
+  schemas:
+    Foo:
+      $ref: ../models/foo.model.yaml
\ No newline at end of file
diff --git a/modules/swagger-parser-v3/src/test/resources/issue-1865/examples/foo.example.yaml b/modules/swagger-parser-v3/src/test/resources/issue-1865/examples/foo.example.yaml
new file mode 100644
index 00000000..43f0089b
--- /dev/null
+++ b/modules/swagger-parser-v3/src/test/resources/issue-1865/examples/foo.example.yaml
@@ -0,0 +1,3 @@
+# broken-ref/examples/foo.example.yaml
+value:
+  foo: bar
\ No newline at end of file
diff --git a/modules/swagger-parser-v3/src/test/resources/issue-1865/models/foo.model.yaml b/modules/swagger-parser-v3/src/test/resources/issue-1865/models/foo.model.yaml
new file mode 100644
index 00000000..8687b262
--- /dev/null
+++ b/modules/swagger-parser-v3/src/test/resources/issue-1865/models/foo.model.yaml
@@ -0,0 +1,6 @@
+#broken-ref/models/foo.model.yaml
+title: Foo
+type: object
+properties:
+  foo:
+    type: string
\ No newline at end of file
diff --git a/modules/swagger-parser-v3/src/test/resources/issue-1865/openapi30.yaml b/modules/swagger-parser-v3/src/test/resources/issue-1865/openapi30.yaml
new file mode 100644
index 00000000..f8895579
--- /dev/null
+++ b/modules/swagger-parser-v3/src/test/resources/issue-1865/openapi30.yaml
@@ -0,0 +1,9 @@
+# broken-ref/openapi30.yaml
+openapi: 3.0.3
+info:
+  title: ref chain
+  version: '1'
+
+paths:
+  /foo:
+    $ref: ./apis/foo.yaml#/paths/~1foo
\ No newline at end of file
diff --git a/modules/swagger-parser/src/test/java/io/swagger/parser/OpenAPIParserTest.java b/modules/swagger-parser/src/test/java/io/swagger/parser/OpenAPIParserTest.java
index 26dde298..cfcdafe2 100644
--- a/modules/swagger-parser/src/test/java/io/swagger/parser/OpenAPIParserTest.java
+++ b/modules/swagger-parser/src/test/java/io/swagger/parser/OpenAPIParserTest.java
@@ -65,26 +65,6 @@ public class OpenAPIParserTest {
         assertNotNull(result.getOpenAPI().getComponents().getSchemas().get("identificacion_usuario_aplicacion"));
     }
 
-    @Test
-    public void testIssue1621() {
-        final ParseOptions parseOptions = new ParseOptions();
-        parseOptions.setResolve(true);
-        parseOptions.setResolveFully(true);
-        parseOptions.setResolveCombinators(false);
-        OpenAPIParser openAPIParser = new OpenAPIParser();
-        SwaggerParseResult swaggerParseResult = openAPIParser.readLocation("issue-1621/example.openapi.yaml", null, parseOptions);
-        assertEquals(0, swaggerParseResult.getMessages().size());
-        OpenAPI api = swaggerParseResult.getOpenAPI();
-        assertEquals("POST Example", api.getPaths()
-                .get("/example")
-                .getPost()
-                .getRequestBody()
-                .getContent()
-                .get("application/json")
-                .getSchema()
-                .getTitle());
-    }
-
     @Test
     public void testIssue749() {
         ParseOptions options = new ParseOptions();