Skip to content

Commit 18d5450

Browse files
committed
Normalize relative ref paths to avoid duplicating schemas
Currently, if an OpenAPI spec contains multiple relative refs to the same file, but those refs are located in different files and use different relative paths to reach the one file, swagger-parser will create a separate, duplicate schema for each relative path rather than reusing the same schema across all equivalent paths. For example, given a spec with the following refs: - In spec root directory, `$ref: ./components/schemas/Thing.yaml` - In components/paths subdirectory, `$ref: ../../components/schemas/Thing.yaml` The parser will produce a `Thing` and a `Thing_1` schema object instead of reusing `Thing` for the second, equivalent reference. This updates the ref processor to resolve relative paths before processing relative refs in order to produce a single `Thing` schema that is reused for all equivalent references.
1 parent 0ab2e8a commit 18d5450

File tree

364 files changed

+15103
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

364 files changed

+15103
-1
lines changed

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

+14
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import io.swagger.v3.parser.ResolverCache;
3030
import io.swagger.v3.parser.models.RefFormat;
3131
import io.swagger.v3.parser.models.RefType;
32+
import io.swagger.v3.parser.util.RefUtils;
33+
3234
import org.apache.commons.io.FilenameUtils;
3335
import org.apache.commons.lang3.StringUtils;
3436
import org.slf4j.LoggerFactory;
@@ -86,6 +88,18 @@ public String processRefToExternalSchema(String $ref, RefFormat refFormat) {
8688
return renamedRef;
8789
}
8890

91+
RefFormat format = computeRefFormat($ref);
92+
if (format.equals(RefFormat.RELATIVE)) {
93+
String normalizedRef = Paths.get($ref).normalize().toString();
94+
System.out.println("Normalized " + $ref + " to " + normalizedRef);
95+
renamedRef = cache.getRenamedRef($ref);
96+
if (renamedRef != null) {
97+
return renamedRef;
98+
} else {
99+
$ref = normalizedRef;
100+
}
101+
}
102+
89103
final Schema schema = cache.loadRef($ref, refFormat, Schema.class);
90104

91105
if(schema == null) {

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

+13-1
Original file line numberDiff line numberDiff line change
@@ -1953,6 +1953,18 @@ public void testRelativePath2() {
19531953
Assert.assertEquals(readResult.getOpenAPI().getPaths().get("/pet/findByTags").getGet().getResponses().get("default").getContent().get("application/json").getSchema().get$ref(), "#/components/schemas/ErrorModel");
19541954
}
19551955

1956+
@Test
1957+
public void testExternalRefsNormalization() throws Exception {
1958+
ParseOptions options = new ParseOptions();
1959+
options.setResolve(true);
1960+
SwaggerParseResult result = new OpenAPIV3Parser()
1961+
.readLocation("src/test/resources/oas3.fetched/openapi3.yaml", null, options);
1962+
1963+
OpenAPI openAPI = result.getOpenAPI();
1964+
Schema localModel = openAPI.getComponents().getSchemas().get("Event_2");
1965+
assertNull(localModel);
1966+
}
1967+
19561968
private OpenAPI doRelativeFileTest(String location) {
19571969
OpenAPIV3Parser parser = new OpenAPIV3Parser();
19581970
ParseOptions options = new ParseOptions();
@@ -3298,4 +3310,4 @@ public void testIssue2081() {
32983310
assertEquals(openAPI.getComponents().getSchemas().get("PetCreate").getRequired().size(), 1);
32993311
assertEquals(openAPI.getComponents().getSchemas().get("PetCreate").getProperties().size(), 2);
33003312
}
3301-
}
3313+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
description: Search by hostname, description, short_id, reservation short_id, tags, plan name, plan slug, facility code, facility name, operating system name, operating system slug, IP addresses.
2+
in: query
3+
name: search
4+
schema:
5+
type: string
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
description: |-
2+
Nested attributes to exclude. Excluded objects will return only the href
3+
attribute. Attribute names can be dotted (up to 3 levels) to exclude deeply
4+
nested objects.
5+
in: query
6+
name: exclude
7+
schema:
8+
items:
9+
type: string
10+
type: array
11+
style: form
12+
explode: false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
description: |-
2+
Nested attributes to include. Included objects will return their full
3+
attributes. Attribute names can be dotted (up to 3 levels) to included deeply
4+
nested objects.
5+
in: query
6+
name: include
7+
schema:
8+
items:
9+
type: string
10+
type: array
11+
style: form
12+
explode: false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
description: Page to return
2+
in: query
3+
name: page
4+
schema:
5+
default: 1
6+
format: int32
7+
maximum: 100000
8+
minimum: 1
9+
type: integer
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
description: Items returned per page
2+
in: query
3+
name: per_page
4+
schema:
5+
default: 10
6+
format: int32
7+
maximum: 1000
8+
minimum: 1
9+
type: integer
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
description: Filter results by name.
2+
in: query
3+
name: name
4+
schema:
5+
type: string
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
content:
2+
application/json:
3+
schema:
4+
oneOf:
5+
- $ref: '../schemas/DedicatedPortCreateInput.yaml'
6+
- $ref: '../schemas/VlanFabricVcCreateInput.yaml'
7+
- $ref: '../schemas/VrfFabricVcCreateInput.yaml'
8+
description: Dedicated port or shared interconnection (also known as Fabric VC) creation request
9+
required: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
content:
2+
application/json:
3+
schema:
4+
$ref: '../schemas/InvitationInput.yaml'
5+
description: Invitation to create
6+
required: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
content:
2+
application/json:
3+
schema:
4+
$ref: '../schemas/PortAssignInput.yaml'
5+
description: 'Virtual Network ID. May be the UUID of the Virtual Network record, or the VLAN value itself (ex: ''1001'').'
6+
required: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
content:
2+
application/json:
3+
schema:
4+
$ref: '../schemas/SSHKeyCreateInput.yaml'
5+
description: ssh key to create
6+
required: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
properties:
2+
address:
3+
type: string
4+
address2:
5+
type: string
6+
city:
7+
type: string
8+
coordinates:
9+
$ref: './Coordinates.yaml'
10+
country:
11+
type: string
12+
state:
13+
type: string
14+
zip_code:
15+
type: string
16+
required:
17+
- address
18+
- zip_code
19+
- country
20+
type: object
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
properties:
2+
namespace:
3+
readOnly: true
4+
type: string
5+
description: Attribute namespace
6+
created_at:
7+
readOnly: true
8+
type: string
9+
format: date-time
10+
description: Datetime when the block was created.
11+
updated_at:
12+
readOnly: true
13+
type: string
14+
format: date-time
15+
description: Datetime when the block was updated.
16+
data:
17+
$ref: "./AttributeData.yaml"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
properties:
2+
latest:
3+
readOnly: true
4+
type: boolean
5+
description: Boolean flag to know if the firmware set is the latest for the model and vendor
6+
model:
7+
readOnly: true
8+
type: string
9+
description: Model on which this firmware set can be applied
10+
vendor:
11+
readOnly: true
12+
type: string
13+
description: Vendor on which this firmware set can be applied
14+
plan:
15+
readOnly: true
16+
type: string
17+
description: Plan where the firmware set can be applied
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
properties:
2+
created_at:
3+
format: date-time
4+
type: string
5+
description:
6+
description: Available only for API keys
7+
type: string
8+
id:
9+
format: uuid
10+
type: string
11+
project:
12+
allOf:
13+
- $ref: './Project.yaml'
14+
- description: Available only for project tokens
15+
read_only:
16+
type: boolean
17+
token:
18+
type: string
19+
updated_at:
20+
format: date-time
21+
type: string
22+
user:
23+
allOf:
24+
- $ref: './User.yaml'
25+
- description: Available only for user tokens
26+
type: object
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
properties:
2+
description:
3+
type: string
4+
read_only:
5+
type: boolean
6+
type: object
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
properties:
2+
api_keys:
3+
items:
4+
$ref: './AuthToken.yaml'
5+
type: array
6+
type: object
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
properties:
2+
address_family:
3+
description: Address family for BGP session.
4+
enum:
5+
- ipv4
6+
- ipv6
7+
example: ipv4
8+
type: string
9+
default_route:
10+
default: false
11+
description: Set the default route policy.
12+
type: boolean
13+
type: object
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
properties:
2+
created_at:
3+
format: date-time
4+
type: string
5+
devices:
6+
items:
7+
$ref: './Href.yaml'
8+
type: array
9+
error_messages:
10+
items:
11+
type: string
12+
type: array
13+
id:
14+
format: uuid
15+
type: string
16+
project:
17+
$ref: './Href.yaml'
18+
quantity:
19+
type: integer
20+
state:
21+
type: string
22+
updated_at:
23+
format: date-time
24+
type: string
25+
type: object
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
properties:
2+
batches:
3+
items:
4+
$ref: './Batch.yaml'
5+
type: array
6+
type: object
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
properties:
2+
asn:
3+
description: Autonomous System Number. ASN is required with Global BGP. With Local
4+
BGP the private ASN, 65000, is assigned.
5+
example: 65000
6+
format: int32
7+
type: integer
8+
created_at:
9+
format: date-time
10+
type: string
11+
deployment_type:
12+
description: |
13+
In a Local BGP deployment, a customer uses an internal ASN to control routes within a single Equinix Metal datacenter. This means that the routes are never advertised to the global Internet. Global BGP, on the other hand, requires a customer to have a registered ASN and IP space.
14+
enum:
15+
- global
16+
- local
17+
example: local
18+
type: string
19+
href:
20+
type: string
21+
id:
22+
format: uuid
23+
type: string
24+
max_prefix:
25+
default: 10
26+
description: The maximum number of route filters allowed per server
27+
type: integer
28+
md5:
29+
description: (Optional) Password for BGP session in plaintext (not a checksum)
30+
nullable: true
31+
type: string
32+
project:
33+
$ref: './Href.yaml'
34+
ranges:
35+
description: The IP block ranges associated to the ASN (Populated in Global BGP
36+
only)
37+
items:
38+
$ref: './GlobalBgpRange.yaml'
39+
type: array
40+
requested_at:
41+
format: date-time
42+
type: string
43+
route_object:
44+
description: Specifies AS-MACRO (aka AS-SET) to use when building client route
45+
filters
46+
type: string
47+
sessions:
48+
description: The direct connections between neighboring routers that want to exchange
49+
routing information.
50+
items:
51+
$ref: './BgpSession.yaml'
52+
type: array
53+
status:
54+
description: Status of the BGP Config. Status "requested" is valid only with the
55+
"global" deployment_type.
56+
enum:
57+
- requested
58+
- enabled
59+
- disabled
60+
type: string
61+
type: object
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
properties:
2+
asn:
3+
type: integer
4+
minimum: 0
5+
maximum: 4294967295
6+
description: Autonomous System Number for local BGP deployment.
7+
example: 65000
8+
deployment_type:
9+
description: Wether the BGP deployment is local or global. Local deployments are configured immediately. Global deployments will need to be reviewed by Equinix Metal engineers.
10+
type: string
11+
example: local
12+
enum:
13+
- local
14+
- global
15+
md5:
16+
type: string
17+
description: |
18+
The plaintext password to share between BGP neighbors as an MD5 checksum:
19+
* must be 10-20 characters long
20+
* may not include punctuation
21+
* must be a combination of numbers and letters
22+
* must contain at least one lowercase, uppercase, and digit character
23+
pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{10,20}$'
24+
use_case:
25+
description: A use case explanation (necessary for global BGP request review).
26+
type: string
27+
required:
28+
- deployment_type
29+
- asn
30+
type: object

0 commit comments

Comments
 (0)