Skip to content

Commit 9c408f0

Browse files
jakubmalekgracekarina
authored andcommitted
#1040 Fixed duplication of external reference path
1 parent 5c480c5 commit 9c408f0

File tree

6 files changed

+169
-10
lines changed

6 files changed

+169
-10
lines changed

modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/ExternalRefProcessor.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,20 @@
2020
import io.swagger.v3.parser.ResolverCache;
2121
import io.swagger.v3.parser.models.RefFormat;
2222
import io.swagger.v3.parser.models.RefType;
23+
import io.swagger.v3.parser.util.RefUtils;
24+
2325
import org.apache.commons.lang3.StringUtils;
2426
import org.slf4j.LoggerFactory;
2527

2628
import java.net.URI;
2729
import java.util.Collection;
2830
import java.util.LinkedHashMap;
2931
import java.util.Map;
32+
import java.util.Objects;
3033

3134
import static io.swagger.v3.parser.util.RefUtils.computeDefinitionName;
3235
import static io.swagger.v3.parser.util.RefUtils.computeRefFormat;
36+
import static io.swagger.v3.parser.util.RefUtils.getExternalPath;
3337
import static io.swagger.v3.parser.util.RefUtils.isAnExternalRefFormat;
3438

3539
public final class ExternalRefProcessor {
@@ -689,8 +693,10 @@ private void processRefSchema(Schema subRef, String externalFile) {
689693
return;
690694
}
691695
String $ref = subRef.get$ref();
696+
String subRefExternalPath = getExternalPath(subRef.get$ref())
697+
.orElse(null);
692698

693-
if (format.equals(RefFormat.RELATIVE)) {
699+
if (format.equals(RefFormat.RELATIVE) && !Objects.equals(subRefExternalPath, externalFile)) {
694700
$ref = constructRef(subRef, externalFile);
695701
subRef.set$ref($ref);
696702
}else {

modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/RefUtils.java

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,27 @@
55
import org.apache.commons.io.IOUtils;
66
import org.apache.commons.lang3.StringUtils;
77

8-
8+
import static java.nio.charset.StandardCharsets.UTF_8;
99

1010
import java.io.FileInputStream;
11+
import java.io.IOException;
12+
import java.io.InputStream;
1113
import java.nio.file.Files;
1214
import java.nio.file.Path;
1315
import java.util.List;
14-
import java.util.Set;
16+
import java.util.Optional;
1517

1618
public class RefUtils {
1719

20+
private static final String REFERENCE_SEPARATOR = "#/";
21+
22+
private RefUtils() {
23+
// static access only
24+
}
25+
1826
public static String computeDefinitionName(String ref) {
1927

20-
final String[] refParts = ref.split("#/");
28+
final String[] refParts = ref.split(REFERENCE_SEPARATOR);
2129

2230
if (refParts.length > 2) {
2331
throw new RuntimeException("Invalid ref format: " + ref);
@@ -43,6 +51,16 @@ public static String computeDefinitionName(String ref) {
4351
return plausibleName;
4452
}
4553

54+
public static Optional<String> getExternalPath(String ref) {
55+
if (ref == null) {
56+
return Optional.empty();
57+
}
58+
return Optional.of(ref.split(REFERENCE_SEPARATOR))
59+
.filter(it -> it.length == 2)
60+
.map(it -> it[0])
61+
.filter(it -> !it.isEmpty());
62+
}
63+
4664
public static boolean isAnExternalRefFormat(RefFormat refFormat) {
4765
return refFormat == RefFormat.URL || refFormat == RefFormat.RELATIVE;
4866
}
@@ -52,9 +70,9 @@ public static RefFormat computeRefFormat(String ref) {
5270
ref = mungedRef(ref);
5371
if(ref.startsWith("http")||ref.startsWith("https")) {
5472
result = RefFormat.URL;
55-
} else if(ref.startsWith("#/")) {
73+
} else if(ref.startsWith(REFERENCE_SEPARATOR)) {
5674
result = RefFormat.INTERNAL;
57-
} else if(ref.startsWith(".") || ref.startsWith("/") || ref.indexOf("#/") > 0) {
75+
} else if(ref.startsWith(".") || ref.startsWith("/") || ref.indexOf(REFERENCE_SEPARATOR) > 0) {
5876
result = RefFormat.RELATIVE;
5977
}
6078

@@ -159,7 +177,7 @@ public static String readExternalRef(String file, RefFormat refFormat, List<Auth
159177
final Path pathToUse = parentDirectory.resolve(file).normalize();
160178

161179
if(Files.exists(pathToUse)) {
162-
result = IOUtils.toString(new FileInputStream(pathToUse.toFile()), "UTF-8");
180+
result = readAll(pathToUse);
163181
} else {
164182
String url = file;
165183
if(url.contains("..")) {
@@ -170,7 +188,7 @@ public static String readExternalRef(String file, RefFormat refFormat, List<Auth
170188
final Path pathToUse2 = parentDirectory.resolve(url).normalize();
171189

172190
if(Files.exists(pathToUse2)) {
173-
result = IOUtils.toString(new FileInputStream(pathToUse2.toFile()), "UTF-8");
191+
result = readAll(pathToUse2);
174192
}
175193
}
176194
if (result == null){
@@ -186,4 +204,10 @@ public static String readExternalRef(String file, RefFormat refFormat, List<Auth
186204
return result;
187205

188206
}
207+
208+
private static String readAll(Path path) throws IOException {
209+
try (InputStream inputStream = new FileInputStream(path.toFile())) {
210+
return IOUtils.toString(inputStream, UTF_8);
211+
}
212+
}
189213
}

modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
import java.util.*;
5353

5454
import static com.github.tomakehurst.wiremock.client.WireMock.*;
55+
import static org.hamcrest.CoreMatchers.equalTo;
56+
import static org.hamcrest.CoreMatchers.instanceOf;
57+
import static org.hamcrest.CoreMatchers.notNullValue;
5558
import static org.junit.Assert.assertThat;
5659
import static org.testng.Assert.*;
5760

@@ -1780,6 +1783,24 @@ public void testValidationIssue() {
17801783
assertThat(result.getMessages().size(), CoreMatchers.is(0));
17811784
}
17821785

1786+
@Test
1787+
public void shouldParseExternalSchemaModelHavingReferenceToItsLocalModel() {
1788+
// given
1789+
String location = "src/test/resources/issue-1040/api.yaml";
1790+
OpenAPIV3Parser tested = new OpenAPIV3Parser();
1791+
1792+
// when
1793+
OpenAPI result = tested.read(location);
1794+
1795+
// then
1796+
Components components = result.getComponents();
1797+
Schema modelSchema = components.getSchemas().get("Value");
1798+
1799+
assertThat(modelSchema, notNullValue());
1800+
assertThat(modelSchema.getProperties().get("id"), instanceOf(Schema.class));
1801+
assertThat(((Schema) modelSchema.getProperties().get("id")).get$ref(), equalTo("#/components/schemas/ValueId"));
1802+
}
1803+
17831804
private static int getDynamicPort() {
17841805
return new Random().ints(10000, 20000).findFirst().getAsInt();
17851806
}

modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/util/RefUtilsTest.java

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@
2424
import java.util.HashMap;
2525
import java.util.List;
2626
import java.util.Map;
27+
import java.util.Optional;
2728

29+
import static java.nio.charset.StandardCharsets.UTF_8;
30+
import static org.hamcrest.CoreMatchers.equalTo;
31+
import static org.hamcrest.CoreMatchers.is;
32+
import static org.junit.Assert.assertThat;
2833
import static org.testng.Assert.assertEquals;
2934
import static org.testng.Assert.assertFalse;
3035
import static org.testng.Assert.assertTrue;
@@ -155,7 +160,7 @@ public void testReadExternalRef_RelativeFileFormat(@Injectable final List<Author
155160
setupRelativeFileExpectations(parentDirectory, pathToUse, file, filePath);
156161

157162
new StrictExpectations() {{
158-
IOUtils.toString((FileInputStream) any, "UTF-8");
163+
IOUtils.toString((FileInputStream) any, UTF_8);
159164
times = 1;
160165
result = expectedResult;
161166

@@ -198,7 +203,7 @@ public void testReadExternalRef_RelativeFileFormat_ExceptionThrown(@Injectable f
198203
setupRelativeFileExpectations(parentDirectory, pathToUse, file, filePath);
199204

200205
new StrictExpectations() {{
201-
IOUtils.toString((FileInputStream) any, "UTF-8");
206+
IOUtils.toString((FileInputStream) any, UTF_8);
202207
times = 1;
203208
result = new IOException();
204209
}};
@@ -293,4 +298,78 @@ public void testPathJoin1() {
293298
// relative locations
294299
assertEquals(ExternalRefProcessor.join("./foo#/definitions/Foo", "./bar#/definitions/Bar"), "./bar#/definitions/Bar");
295300
}
301+
302+
@Test
303+
public void shouldReturnEmptyExternalPathForInternalReference() {
304+
// given
305+
String ref = "#/components/test";
306+
307+
// when
308+
Optional<String> externalPath = RefUtils.getExternalPath(ref);
309+
310+
// then
311+
assertThat(externalPath.isPresent(), is(false));
312+
}
313+
314+
@Test
315+
public void shouldReturnEmptyExternalPathForNullReference() {
316+
// given
317+
String ref = null;
318+
319+
// when
320+
Optional<String> externalPath = RefUtils.getExternalPath(ref);
321+
322+
// then
323+
assertThat(externalPath.isPresent(), is(false));
324+
}
325+
326+
@Test
327+
public void shouldReturnEmptyExternalPathForEmptyReference() {
328+
// given
329+
String ref = "";
330+
331+
// when
332+
Optional<String> externalPath = RefUtils.getExternalPath(ref);
333+
334+
// then
335+
assertThat(externalPath.isPresent(), is(false));
336+
}
337+
338+
@Test
339+
public void shouldReturnEmptyExternalPathForInvalidReference() {
340+
// given
341+
String ref = "test";
342+
343+
// when
344+
Optional<String> externalPath = RefUtils.getExternalPath(ref);
345+
346+
// then
347+
assertThat(externalPath.isPresent(), is(false));
348+
}
349+
350+
@Test
351+
public void shouldReturnExternalPathForFileReference() {
352+
// given
353+
String ref = "test.yaml#/components/test";
354+
355+
// when
356+
Optional<String> externalPath = RefUtils.getExternalPath(ref);
357+
358+
// then
359+
assertThat(externalPath.isPresent(), is(true));
360+
assertThat(externalPath.get(), equalTo("test.yaml"));
361+
}
362+
363+
@Test
364+
public void shouldReturnExternalPathForHttpReference() {
365+
// given
366+
String ref = "http://localhost/schema.json#/components/test";
367+
368+
// when
369+
Optional<String> externalPath = RefUtils.getExternalPath(ref);
370+
371+
// then
372+
assertThat(externalPath.isPresent(), is(true));
373+
assertThat(externalPath.get(), equalTo("http://localhost/schema.json"));
374+
}
296375
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
openapi: 3.0.0
2+
info:
3+
title: Test
4+
version: 1.0.0
5+
6+
paths:
7+
/value:
8+
get:
9+
operationId: getValues
10+
responses:
11+
200:
12+
description: Successful response
13+
content:
14+
application/json:
15+
schema:
16+
$ref: 'lib/lib.yaml#/components/schemas/Value'
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
components:
2+
schemas:
3+
ValueId:
4+
type: object
5+
properties:
6+
id:
7+
type: integer
8+
format: int64
9+
Value:
10+
type: object
11+
properties:
12+
id:
13+
$ref: '#/components/schemas/ValueId'

0 commit comments

Comments
 (0)