Skip to content

Commit 9a9a1e4

Browse files
committed
v32: bundle support
1 parent d53d6e4 commit 9a9a1e4

File tree

18 files changed

+352
-17
lines changed

18 files changed

+352
-17
lines changed

openapi-parser/src/main/java/io/openapiparser/OpenApiBundler.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.net.URI;
1414
import java.util.*;
1515

16+
import static io.openapiprocessor.jsonschema.support.Null.nonNull;
1617
import static io.openapiprocessor.jsonschema.support.Null.requiresNonNull;
1718
import static io.openapiprocessor.jsonschema.support.Types.*;
1819
import static io.openapiprocessor.jsonschema.schema.Scope.createScope;
@@ -38,17 +39,17 @@ public class OpenApiBundler {
3839
private final Map<String, @Nullable Object> links = new LinkedHashMap<> ();
3940
private final Map<String, @Nullable Object> callbacks = new LinkedHashMap<> ();
4041
private final Map<String, @Nullable Object> pathItems = new LinkedHashMap<> ();
42+
private final Map<String, @Nullable Object> mediaTypes = new LinkedHashMap<> ();
4143

4244
public OpenApiBundler (Context context, DocumentStore documents, Bucket root) {
4345
this.context = context;
4446
this.documents = documents.copy ();
4547
this.root = root;
4648
this.rootDocumentUri = root.getScope ().getDocumentUri ();
47-
this.rootDocument = documents.get (rootDocumentUri);
49+
this.rootDocument = nonNull(documents.get (rootDocumentUri));
4850
this.version = OpenApiVersionParser.parseVersion(rootDocument);
4951
}
5052

51-
5253
public Map<String, @Nullable Object> bundle () {
5354
Bucket bundled = createBucket(root.getScope(), rootDocument, root.getLocation());
5455
walkBucket (bundled);
@@ -84,6 +85,7 @@ private void mergeMaps (Map<String, @Nullable Object> components) {
8485
mergeMap (components, "links", links);
8586
mergeMap (components, "callbacks", callbacks);
8687
mergeMap (components, "pathItems", pathItems);
88+
mergeMap (components, "mediaTypes", mediaTypes);
8789
}
8890

8991
private void mergeMap (
@@ -197,6 +199,10 @@ private boolean isExternalDocument (URI documentUri) {
197199
} else if (isCallbacksRef (location) && external) {
198200
bundleCallback (bucketValues, refName, refValue);
199201

202+
} else if(isMediaTypeRef(location) && external) {
203+
if(version == OpenApiVersion.V32) {
204+
bundleMediaType(bucketValues, refName, refValue);
205+
}
200206
} else if (isPathRef (location) && external) {
201207
if(version == OpenApiVersion.V30) {
202208
result = bundlePathItem30(bucketValues, refValue);
@@ -291,6 +297,11 @@ private Runnable bundlePathItem30(Map<String, @Nullable Object> rawValues, RawVa
291297
};
292298
}
293299

300+
private void bundleMediaType (Map<String, @Nullable Object> rawValues, String refName, RawValue refValue) {
301+
mediaTypes.put (refName, refValue.getValue ());
302+
rawValues.put (Keywords.REF, createRefPointer ("mediaTypes", refName));
303+
}
304+
294305
private static RawValue getRefValue (Bucket documentBucket, JsonPointer refPointer) {
295306
RawValue refValue = documentBucket.getRawValue (refPointer);
296307
if (refValue == null) {
@@ -467,4 +478,12 @@ private boolean isPathRef (JsonPointer location) {
467478
&& tokens.get (0).equals ("paths")
468479
&& tokens.get (2).equals (Keywords.REF);
469480
}
481+
482+
private boolean isMediaTypeRef (JsonPointer location) {
483+
// check for /**/content/*/$ref
484+
List<String> tokens = location.getTokens ();
485+
return tokens.size () > 3
486+
&& tokens.get (tokens.size () - 3).equals ("content")
487+
&& tokens.get (tokens.size () - 1).equals (Keywords.REF);
488+
}
470489
}

openapi-parser/src/test/kotlin/io/openapiparser/OpenApiBundlerSpec.kt

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ class OpenApiBundlerSpec : FreeSpec({
4444
return Bucket.createBucket(result.scope, bundle)!!
4545
}
4646

47+
fun bundle32(result: ResolverResult): Bucket {
48+
val context = Context(result.scope, result.registry)
49+
val bucket = Bucket(result.scope, asObject(result.document))
50+
val api = OpenApiResult32(context, bucket, result.documents)
51+
val bundle = api.bundle()
52+
return Bucket.createBucket(result.scope, bundle)!!
53+
}
54+
4755
fun getObject(bucket: Bucket, jsonPointer: String): Map<String, Any?> {
4856
return asObject(bucket.getRawValue(from(jsonPointer))!!.value)
4957
}
@@ -53,7 +61,8 @@ class OpenApiBundlerSpec : FreeSpec({
5361
withData(
5462
mapOf(
5563
$$"bundle handles $ref loop, 30" to Data("/bundle-ref-loop/openapi30.yaml", ::bundle30),
56-
$$"bundle handles $ref loop, 31" to Data("/bundle-ref-loop/openapi31.yaml", ::bundle31)
64+
$$"bundle handles $ref loop, 31" to Data("/bundle-ref-loop/openapi31.yaml", ::bundle31),
65+
$$"bundle handles $ref loop, 32" to Data("/bundle-ref-loop/openapi32.yaml", ::bundle32)
5766
)
5867
) { (api, bundle) ->
5968
val result = resolve(api)
@@ -69,7 +78,8 @@ class OpenApiBundlerSpec : FreeSpec({
6978
withData(
7079
mapOf(
7180
$$"bundle schema $ref, 30" to Data("/bundle-ref-schema/openapi30.yaml", ::bundle30),
72-
$$"bundle schema $ref, 31" to Data("/bundle-ref-schema/openapi31.yaml", ::bundle31)
81+
$$"bundle schema $ref, 31" to Data("/bundle-ref-schema/openapi31.yaml", ::bundle31),
82+
$$"bundle schema $ref, 32" to Data("/bundle-ref-schema/openapi32.yaml", ::bundle32)
7383
)
7484
) { (api, bundle) ->
7585
val result = resolve(api)
@@ -85,7 +95,8 @@ class OpenApiBundlerSpec : FreeSpec({
8595
withData(
8696
mapOf(
8797
$$"bundle response $ref, 30" to Data("/bundle-ref-response/openapi30.yaml", ::bundle30),
88-
$$"bundle response $ref, 31" to Data("/bundle-ref-response/openapi31.yaml", ::bundle31)
98+
$$"bundle response $ref, 31" to Data("/bundle-ref-response/openapi31.yaml", ::bundle31),
99+
$$"bundle response $ref, 32" to Data("/bundle-ref-response/openapi32.yaml", ::bundle32)
89100
)
90101
) { (api, bundle) ->
91102
val result = resolve(api)
@@ -102,7 +113,8 @@ class OpenApiBundlerSpec : FreeSpec({
102113
withData(
103114
mapOf(
104115
$$"bundle parameter $ref, 30" to Data("/bundle-ref-parameter/openapi30.yaml", ::bundle30),
105-
$$"bundle parameter $ref, 31" to Data("/bundle-ref-parameter/openapi31.yaml", ::bundle31)
116+
$$"bundle parameter $ref, 31" to Data("/bundle-ref-parameter/openapi31.yaml", ::bundle31),
117+
$$"bundle parameter $ref, 32" to Data("/bundle-ref-parameter/openapi32.yaml", ::bundle32)
106118
)
107119
) { (api, bundle) ->
108120
val result = resolve(api)
@@ -119,7 +131,8 @@ class OpenApiBundlerSpec : FreeSpec({
119131
withData(
120132
mapOf(
121133
$$"bundle example $ref, 30" to Data("/bundle-ref-example/openapi30.yaml", ::bundle30),
122-
$$"bundle example $ref, 31" to Data("/bundle-ref-example/openapi31.yaml", ::bundle31)
134+
$$"bundle example $ref, 31" to Data("/bundle-ref-example/openapi31.yaml", ::bundle31),
135+
$$"bundle example $ref, 32" to Data("/bundle-ref-example/openapi32.yaml", ::bundle32)
123136
)
124137
) { (api, bundle) ->
125138
val result = resolve(api)
@@ -136,7 +149,8 @@ class OpenApiBundlerSpec : FreeSpec({
136149
withData(
137150
mapOf(
138151
$$"bundle request body $ref, 30" to Data("/bundle-ref-request-body/openapi30.yaml", ::bundle30),
139-
$$"bundle request body $ref, 31" to Data("/bundle-ref-request-body/openapi31.yaml", ::bundle31)
152+
$$"bundle request body $ref, 31" to Data("/bundle-ref-request-body/openapi31.yaml", ::bundle31),
153+
$$"bundle request body $ref, 32" to Data("/bundle-ref-request-body/openapi32.yaml", ::bundle32)
140154
)
141155
) { (api, bundle) ->
142156
val result = resolve(api)
@@ -153,7 +167,8 @@ class OpenApiBundlerSpec : FreeSpec({
153167
withData(
154168
mapOf(
155169
$$"bundle header $ref, 30" to Data("/bundle-ref-header/openapi30.yaml", ::bundle30),
156-
$$"bundle header $ref, 31" to Data("/bundle-ref-header/openapi31.yaml", ::bundle31)
170+
$$"bundle header $ref, 31" to Data("/bundle-ref-header/openapi31.yaml", ::bundle31),
171+
$$"bundle header $ref, 32" to Data("/bundle-ref-header/openapi32.yaml", ::bundle32)
157172
)
158173
) { (api, bundle) ->
159174
val result = resolve(api)
@@ -170,7 +185,8 @@ class OpenApiBundlerSpec : FreeSpec({
170185
withData(
171186
mapOf(
172187
$$"bundle security scheme $ref, 30" to Data("/bundle-ref-security-scheme/openapi30.yaml", ::bundle30),
173-
$$"bundle security scheme $ref, 31" to Data("/bundle-ref-security-scheme/openapi31.yaml", ::bundle31)
188+
$$"bundle security scheme $ref, 31" to Data("/bundle-ref-security-scheme/openapi31.yaml", ::bundle31),
189+
$$"bundle security scheme $ref, 32" to Data("/bundle-ref-security-scheme/openapi32.yaml", ::bundle32)
174190
)
175191
) { (api, bundle) ->
176192
val result = resolve(api)
@@ -184,7 +200,8 @@ class OpenApiBundlerSpec : FreeSpec({
184200
withData(
185201
mapOf(
186202
$$"bundle link $ref, 30" to Data("/bundle-ref-link/openapi30.yaml", ::bundle30),
187-
$$"bundle link $ref, 31" to Data("/bundle-ref-link/openapi31.yaml", ::bundle31)
203+
$$"bundle link $ref, 31" to Data("/bundle-ref-link/openapi31.yaml", ::bundle31),
204+
$$"bundle link $ref, 32" to Data("/bundle-ref-link/openapi32.yaml", ::bundle32)
188205
)
189206
) { (api, bundle) ->
190207
val result = resolve(api)
@@ -201,13 +218,14 @@ class OpenApiBundlerSpec : FreeSpec({
201218
withData(
202219
mapOf(
203220
$$"bundle callback $ref, 30" to Data("/bundle-ref-callback/openapi30.yaml", ::bundle30),
204-
$$"bundle callback $ref, 31" to Data("/bundle-ref-callback/openapi31.yaml", ::bundle31)
221+
$$"bundle callback $ref, 31" to Data("/bundle-ref-callback/openapi31.yaml", ::bundle31),
222+
$$"bundle callback $ref, 32" to Data("/bundle-ref-callback/openapi32.yaml", ::bundle32)
205223
)
206224
) { (api, bundle) ->
207225
val result = resolve(api)
208226
val bundled = bundle(result)
209227

210-
val ref = getObject(bundled, "/paths/~1foo/get/callbacks/\$url")
228+
val ref = getObject(bundled, $$"/paths/~1foo/get/callbacks/$url")
211229
ref.size shouldBe 1
212230
ref[$$"$ref"].shouldBe("#/components/callbacks/Foo")
213231

@@ -234,7 +252,8 @@ class OpenApiBundlerSpec : FreeSpec({
234252

235253
withData(
236254
mapOf(
237-
$$"bundle path $ref, 31" to Data("/bundle-ref-path-item/openapi31.yaml", ::bundle31)
255+
$$"bundle path $ref, 31" to Data("/bundle-ref-path-item/openapi31.yaml", ::bundle31),
256+
$$"bundle path $ref, 32" to Data("/bundle-ref-path-item/openapi32.yaml", ::bundle32)
238257
)
239258
) { (api, bundle) ->
240259
val result = resolve(api)
@@ -281,7 +300,8 @@ class OpenApiBundlerSpec : FreeSpec({
281300

282301
withData(
283302
mapOf(
284-
$$"bundle nested $ref, 31" to Data("/bundle-ref-nested/openapi31.yaml", ::bundle31)
303+
$$"bundle nested $ref, 31" to Data("/bundle-ref-nested/openapi31.yaml", ::bundle31),
304+
$$"bundle nested $ref, 32" to Data("/bundle-ref-nested/openapi32.yaml", ::bundle32)
285305
)
286306
) { (api, bundle) ->
287307
val result = resolve(api)
@@ -312,7 +332,8 @@ class OpenApiBundlerSpec : FreeSpec({
312332
withData(
313333
mapOf(
314334
"does not override existing component, 30" to Data("/bundle-ref-components/openapi30.yaml", ::bundle30),
315-
"does not override existing component, 31" to Data("/bundle-ref-components/openapi31.yaml", ::bundle31)
335+
"does not override existing component, 31" to Data("/bundle-ref-components/openapi31.yaml", ::bundle31),
336+
"does not override existing component, 32" to Data("/bundle-ref-components/openapi32.yaml", ::bundle32)
316337
)
317338
) { (api, bundle) ->
318339
val result = resolve(api)
@@ -325,6 +346,23 @@ class OpenApiBundlerSpec : FreeSpec({
325346
existing.shouldNotBeNull()
326347
}
327348

349+
withData(
350+
mapOf(
351+
$$"bundle media type $ref, 32" to Data("/bundle-ref-mediatype/openapi32.yaml", ::bundle32)
352+
)
353+
) { (api, bundle) ->
354+
val result = resolve(api)
355+
val bundled = bundle(result)
356+
357+
val requestBody = getObject(bundled, "/paths/~1foo/get/requestBody/content/application~1json")
358+
requestBody[$$"$ref"].shouldBe("#/components/mediaTypes/Foo")
359+
getObject(bundled, "/components/mediaTypes/Foo").shouldNotBeNull()
360+
361+
val response = getObject(bundled, "/paths/~1foo/get/responses/200/content/application~1json")
362+
response[$$"$ref"].shouldBe("#/components/mediaTypes/Bar")
363+
getObject(bundled, "/components/mediaTypes/Bar").shouldNotBeNull()
364+
}
365+
328366
// should not modify in-document refs
329367
// conflict if name of bundled component is already used
330368
})
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
openapi: 3.2.0
2+
info:
3+
title: test callback $ref bundling
4+
version: 1.0.0
5+
6+
paths:
7+
8+
/foo:
9+
get:
10+
responses:
11+
'204':
12+
description: none
13+
callbacks:
14+
'$url':
15+
$ref: 'foo.yaml#/Foo'
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
openapi: 3.2.0
2+
info:
3+
title: test merge of existing and bundled components
4+
version: 1.0.0
5+
6+
paths:
7+
/foo:
8+
get:
9+
responses:
10+
'200':
11+
description: none
12+
content:
13+
application/json:
14+
schema:
15+
$ref: 'foo.yaml#/Foo'
16+
application/x-yaml:
17+
schema:
18+
$ref: '#/components/schemas/Bar'
19+
20+
components:
21+
schemas:
22+
Bar:
23+
type: object
24+
properties:
25+
bar:
26+
type: string
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
openapi: 3.2.0
2+
info:
3+
title: test examples $ref bundling
4+
version: 1.0.0
5+
6+
paths:
7+
8+
/foo:
9+
get:
10+
parameters:
11+
- description: parameter
12+
name: foo
13+
in: query
14+
schema:
15+
type: string
16+
examples:
17+
foo:
18+
$ref: 'foo.yaml#/Foo'
19+
responses:
20+
'204':
21+
description: none
22+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
openapi: 3.2.0
2+
info:
3+
title: test header $ref bundling
4+
version: 1.0.0
5+
6+
paths:
7+
8+
/foo:
9+
get:
10+
responses:
11+
'204':
12+
description: none
13+
headers:
14+
foo:
15+
$ref: 'foo.yaml#/Foo'
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
openapi: 3.2.0
2+
info:
3+
title: test link $ref bundling
4+
version: 1.0.0
5+
6+
paths:
7+
8+
/foo:
9+
get:
10+
operationId: foo
11+
responses:
12+
'204':
13+
description: none
14+
links:
15+
foo:
16+
$ref: 'foo.yaml#/Foo'
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
openapi: 3.2.0
2+
info:
3+
title: test $ref loop
4+
version: 1.0.0
5+
6+
paths:
7+
8+
/self-reference:
9+
get:
10+
responses:
11+
'200':
12+
description: none
13+
content:
14+
application/json:
15+
schema:
16+
$ref: '#/components/schemas/Self'
17+
18+
/nested-self-reference:
19+
get:
20+
responses:
21+
'200':
22+
description: none
23+
content:
24+
application/json:
25+
schema:
26+
$ref: '#/components/schemas/Foo'
27+
28+
components:
29+
schemas:
30+
31+
Self:
32+
type: object
33+
properties:
34+
self:
35+
$ref: '#/components/schemas/Self'
36+
37+
Foo:
38+
type: object
39+
properties:
40+
child:
41+
$ref: '#/components/schemas/Bar'
42+
43+
Bar:
44+
type: object
45+
properties:
46+
parent:
47+
$ref: '#/components/schemas/Foo'

0 commit comments

Comments
 (0)