Skip to content

Commit 7f75486

Browse files
authored
Internalize recursive external references #618 (#655)
1 parent 834a791 commit 7f75486

File tree

3 files changed

+191
-85
lines changed

3 files changed

+191
-85
lines changed

openapi3/internalize_refs.go

Lines changed: 90 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -45,96 +45,100 @@ func parametersMapNames(s ParametersMap) []string {
4545
return out
4646
}
4747

48-
func isExternalRef(ref string) bool {
49-
return ref != "" && !strings.HasPrefix(ref, "#/components/")
48+
func isExternalRef(ref string, parentIsExternal bool) bool {
49+
return ref != "" && (!strings.HasPrefix(ref, "#/components/") || parentIsExternal)
5050
}
5151

52-
func (doc *T) addSchemaToSpec(s *SchemaRef, refNameResolver RefNameResolver) {
53-
if s == nil || !isExternalRef(s.Ref) {
54-
return
52+
func (doc *T) addSchemaToSpec(s *SchemaRef, refNameResolver RefNameResolver, parentIsExternal bool) bool {
53+
if s == nil || !isExternalRef(s.Ref, parentIsExternal) {
54+
return false
5555
}
5656

5757
name := refNameResolver(s.Ref)
5858
if _, ok := doc.Components.Schemas[name]; ok {
5959
s.Ref = "#/components/schemas/" + name
60-
return
60+
return true
6161
}
6262

6363
if doc.Components.Schemas == nil {
6464
doc.Components.Schemas = make(Schemas)
6565
}
6666
doc.Components.Schemas[name] = s.Value.NewRef()
6767
s.Ref = "#/components/schemas/" + name
68+
return true
6869
}
6970

70-
func (doc *T) addParameterToSpec(p *ParameterRef, refNameResolver RefNameResolver) {
71-
if p == nil || !isExternalRef(p.Ref) {
72-
return
71+
func (doc *T) addParameterToSpec(p *ParameterRef, refNameResolver RefNameResolver, parentIsExternal bool) bool {
72+
if p == nil || !isExternalRef(p.Ref, parentIsExternal) {
73+
return false
7374
}
7475
name := refNameResolver(p.Ref)
7576
if _, ok := doc.Components.Parameters[name]; ok {
7677
p.Ref = "#/components/parameters/" + name
77-
return
78+
return true
7879
}
7980

8081
if doc.Components.Parameters == nil {
8182
doc.Components.Parameters = make(ParametersMap)
8283
}
8384
doc.Components.Parameters[name] = &ParameterRef{Value: p.Value}
8485
p.Ref = "#/components/parameters/" + name
86+
return true
8587
}
8688

87-
func (doc *T) addHeaderToSpec(h *HeaderRef, refNameResolver RefNameResolver) {
88-
if h == nil || !isExternalRef(h.Ref) {
89-
return
89+
func (doc *T) addHeaderToSpec(h *HeaderRef, refNameResolver RefNameResolver, parentIsExternal bool) bool {
90+
if h == nil || !isExternalRef(h.Ref, parentIsExternal) {
91+
return false
9092
}
9193
name := refNameResolver(h.Ref)
9294
if _, ok := doc.Components.Headers[name]; ok {
9395
h.Ref = "#/components/headers/" + name
94-
return
96+
return true
9597
}
9698
if doc.Components.Headers == nil {
9799
doc.Components.Headers = make(Headers)
98100
}
99101
doc.Components.Headers[name] = &HeaderRef{Value: h.Value}
100102
h.Ref = "#/components/headers/" + name
103+
return true
101104
}
102105

103-
func (doc *T) addRequestBodyToSpec(r *RequestBodyRef, refNameResolver RefNameResolver) {
104-
if r == nil || !isExternalRef(r.Ref) {
105-
return
106+
func (doc *T) addRequestBodyToSpec(r *RequestBodyRef, refNameResolver RefNameResolver, parentIsExternal bool) bool {
107+
if r == nil || !isExternalRef(r.Ref, parentIsExternal) {
108+
return false
106109
}
107110
name := refNameResolver(r.Ref)
108111
if _, ok := doc.Components.RequestBodies[name]; ok {
109112
r.Ref = "#/components/requestBodies/" + name
110-
return
113+
return true
111114
}
112115
if doc.Components.RequestBodies == nil {
113116
doc.Components.RequestBodies = make(RequestBodies)
114117
}
115118
doc.Components.RequestBodies[name] = &RequestBodyRef{Value: r.Value}
116119
r.Ref = "#/components/requestBodies/" + name
120+
return true
117121
}
118122

119-
func (doc *T) addResponseToSpec(r *ResponseRef, refNameResolver RefNameResolver) {
120-
if r == nil || !isExternalRef(r.Ref) {
121-
return
123+
func (doc *T) addResponseToSpec(r *ResponseRef, refNameResolver RefNameResolver, parentIsExternal bool) bool {
124+
if r == nil || !isExternalRef(r.Ref, parentIsExternal) {
125+
return false
122126
}
123127
name := refNameResolver(r.Ref)
124128
if _, ok := doc.Components.Responses[name]; ok {
125129
r.Ref = "#/components/responses/" + name
126-
return
130+
return true
127131
}
128132
if doc.Components.Responses == nil {
129133
doc.Components.Responses = make(Responses)
130134
}
131135
doc.Components.Responses[name] = &ResponseRef{Value: r.Value}
132136
r.Ref = "#/components/responses/" + name
133-
137+
return true
134138
}
135139

136-
func (doc *T) addSecuritySchemeToSpec(ss *SecuritySchemeRef, refNameResolver RefNameResolver) {
137-
if ss == nil || !isExternalRef(ss.Ref) {
140+
func (doc *T) addSecuritySchemeToSpec(ss *SecuritySchemeRef, refNameResolver RefNameResolver, parentIsExternal bool) {
141+
if ss == nil || !isExternalRef(ss.Ref, parentIsExternal) {
138142
return
139143
}
140144
name := refNameResolver(ss.Ref)
@@ -150,8 +154,8 @@ func (doc *T) addSecuritySchemeToSpec(ss *SecuritySchemeRef, refNameResolver Ref
150154

151155
}
152156

153-
func (doc *T) addExampleToSpec(e *ExampleRef, refNameResolver RefNameResolver) {
154-
if e == nil || !isExternalRef(e.Ref) {
157+
func (doc *T) addExampleToSpec(e *ExampleRef, refNameResolver RefNameResolver, parentIsExternal bool) {
158+
if e == nil || !isExternalRef(e.Ref, parentIsExternal) {
155159
return
156160
}
157161
name := refNameResolver(e.Ref)
@@ -167,8 +171,8 @@ func (doc *T) addExampleToSpec(e *ExampleRef, refNameResolver RefNameResolver) {
167171

168172
}
169173

170-
func (doc *T) addLinkToSpec(l *LinkRef, refNameResolver RefNameResolver) {
171-
if l == nil || !isExternalRef(l.Ref) {
174+
func (doc *T) addLinkToSpec(l *LinkRef, refNameResolver RefNameResolver, parentIsExternal bool) {
175+
if l == nil || !isExternalRef(l.Ref, parentIsExternal) {
172176
return
173177
}
174178
name := refNameResolver(l.Ref)
@@ -184,9 +188,9 @@ func (doc *T) addLinkToSpec(l *LinkRef, refNameResolver RefNameResolver) {
184188

185189
}
186190

187-
func (doc *T) addCallbackToSpec(c *CallbackRef, refNameResolver RefNameResolver) {
188-
if c == nil || !isExternalRef(c.Ref) {
189-
return
191+
func (doc *T) addCallbackToSpec(c *CallbackRef, refNameResolver RefNameResolver, parentIsExternal bool) bool {
192+
if c == nil || !isExternalRef(c.Ref, parentIsExternal) {
193+
return false
190194
}
191195
name := refNameResolver(c.Ref)
192196
if _, ok := doc.Components.Callbacks[name]; ok {
@@ -197,118 +201,119 @@ func (doc *T) addCallbackToSpec(c *CallbackRef, refNameResolver RefNameResolver)
197201
}
198202
doc.Components.Callbacks[name] = &CallbackRef{Value: c.Value}
199203
c.Ref = "#/components/callbacks/" + name
204+
return true
200205
}
201206

202-
func (doc *T) derefSchema(s *Schema, refNameResolver RefNameResolver) {
207+
func (doc *T) derefSchema(s *Schema, refNameResolver RefNameResolver, parentIsExternal bool) {
203208
if s == nil || doc.isVisitedSchema(s) {
204209
return
205210
}
206211

207212
for _, list := range []SchemaRefs{s.AllOf, s.AnyOf, s.OneOf} {
208213
for _, s2 := range list {
209-
doc.addSchemaToSpec(s2, refNameResolver)
214+
isExternal := doc.addSchemaToSpec(s2, refNameResolver, parentIsExternal)
210215
if s2 != nil {
211-
doc.derefSchema(s2.Value, refNameResolver)
216+
doc.derefSchema(s2.Value, refNameResolver, isExternal || parentIsExternal)
212217
}
213218
}
214219
}
215220
for _, s2 := range s.Properties {
216-
doc.addSchemaToSpec(s2, refNameResolver)
221+
isExternal := doc.addSchemaToSpec(s2, refNameResolver, parentIsExternal)
217222
if s2 != nil {
218-
doc.derefSchema(s2.Value, refNameResolver)
223+
doc.derefSchema(s2.Value, refNameResolver, isExternal || parentIsExternal)
219224
}
220225
}
221226
for _, ref := range []*SchemaRef{s.Not, s.AdditionalProperties, s.Items} {
222-
doc.addSchemaToSpec(ref, refNameResolver)
227+
isExternal := doc.addSchemaToSpec(ref, refNameResolver, parentIsExternal)
223228
if ref != nil {
224-
doc.derefSchema(ref.Value, refNameResolver)
229+
doc.derefSchema(ref.Value, refNameResolver, isExternal || parentIsExternal)
225230
}
226231
}
227232
}
228233

229-
func (doc *T) derefHeaders(hs Headers, refNameResolver RefNameResolver) {
234+
func (doc *T) derefHeaders(hs Headers, refNameResolver RefNameResolver, parentIsExternal bool) {
230235
for _, h := range hs {
231-
doc.addHeaderToSpec(h, refNameResolver)
236+
isExternal := doc.addHeaderToSpec(h, refNameResolver, parentIsExternal)
232237
if doc.isVisitedHeader(h.Value) {
233238
continue
234239
}
235-
doc.derefParameter(h.Value.Parameter, refNameResolver)
240+
doc.derefParameter(h.Value.Parameter, refNameResolver, parentIsExternal || isExternal)
236241
}
237242
}
238243

239-
func (doc *T) derefExamples(es Examples, refNameResolver RefNameResolver) {
244+
func (doc *T) derefExamples(es Examples, refNameResolver RefNameResolver, parentIsExternal bool) {
240245
for _, e := range es {
241-
doc.addExampleToSpec(e, refNameResolver)
246+
doc.addExampleToSpec(e, refNameResolver, parentIsExternal)
242247
}
243248
}
244249

245-
func (doc *T) derefContent(c Content, refNameResolver RefNameResolver) {
250+
func (doc *T) derefContent(c Content, refNameResolver RefNameResolver, parentIsExternal bool) {
246251
for _, mediatype := range c {
247-
doc.addSchemaToSpec(mediatype.Schema, refNameResolver)
252+
isExternal := doc.addSchemaToSpec(mediatype.Schema, refNameResolver, parentIsExternal)
248253
if mediatype.Schema != nil {
249-
doc.derefSchema(mediatype.Schema.Value, refNameResolver)
254+
doc.derefSchema(mediatype.Schema.Value, refNameResolver, isExternal || parentIsExternal)
250255
}
251-
doc.derefExamples(mediatype.Examples, refNameResolver)
256+
doc.derefExamples(mediatype.Examples, refNameResolver, parentIsExternal)
252257
for _, e := range mediatype.Encoding {
253-
doc.derefHeaders(e.Headers, refNameResolver)
258+
doc.derefHeaders(e.Headers, refNameResolver, parentIsExternal)
254259
}
255260
}
256261
}
257262

258-
func (doc *T) derefLinks(ls Links, refNameResolver RefNameResolver) {
263+
func (doc *T) derefLinks(ls Links, refNameResolver RefNameResolver, parentIsExternal bool) {
259264
for _, l := range ls {
260-
doc.addLinkToSpec(l, refNameResolver)
265+
doc.addLinkToSpec(l, refNameResolver, parentIsExternal)
261266
}
262267
}
263268

264-
func (doc *T) derefResponses(es Responses, refNameResolver RefNameResolver) {
269+
func (doc *T) derefResponses(es Responses, refNameResolver RefNameResolver, parentIsExternal bool) {
265270
for _, e := range es {
266-
doc.addResponseToSpec(e, refNameResolver)
271+
isExternal := doc.addResponseToSpec(e, refNameResolver, parentIsExternal)
267272
if e.Value != nil {
268-
doc.derefHeaders(e.Value.Headers, refNameResolver)
269-
doc.derefContent(e.Value.Content, refNameResolver)
270-
doc.derefLinks(e.Value.Links, refNameResolver)
273+
doc.derefHeaders(e.Value.Headers, refNameResolver, isExternal || parentIsExternal)
274+
doc.derefContent(e.Value.Content, refNameResolver, isExternal || parentIsExternal)
275+
doc.derefLinks(e.Value.Links, refNameResolver, isExternal || parentIsExternal)
271276
}
272277
}
273278
}
274279

275-
func (doc *T) derefParameter(p Parameter, refNameResolver RefNameResolver) {
276-
doc.addSchemaToSpec(p.Schema, refNameResolver)
277-
doc.derefContent(p.Content, refNameResolver)
280+
func (doc *T) derefParameter(p Parameter, refNameResolver RefNameResolver, parentIsExternal bool) {
281+
isExternal := doc.addSchemaToSpec(p.Schema, refNameResolver, parentIsExternal)
282+
doc.derefContent(p.Content, refNameResolver, parentIsExternal)
278283
if p.Schema != nil {
279-
doc.derefSchema(p.Schema.Value, refNameResolver)
284+
doc.derefSchema(p.Schema.Value, refNameResolver, isExternal || parentIsExternal)
280285
}
281286
}
282287

283-
func (doc *T) derefRequestBody(r RequestBody, refNameResolver RefNameResolver) {
284-
doc.derefContent(r.Content, refNameResolver)
288+
func (doc *T) derefRequestBody(r RequestBody, refNameResolver RefNameResolver, parentIsExternal bool) {
289+
doc.derefContent(r.Content, refNameResolver, parentIsExternal)
285290
}
286291

287-
func (doc *T) derefPaths(paths map[string]*PathItem, refNameResolver RefNameResolver) {
292+
func (doc *T) derefPaths(paths map[string]*PathItem, refNameResolver RefNameResolver, parentIsExternal bool) {
288293
for _, ops := range paths {
289294
// inline full operations
290295
ops.Ref = ""
291296

292297
for _, param := range ops.Parameters {
293-
doc.addParameterToSpec(param, refNameResolver)
298+
doc.addParameterToSpec(param, refNameResolver, parentIsExternal)
294299
}
295300

296301
for _, op := range ops.Operations() {
297-
doc.addRequestBodyToSpec(op.RequestBody, refNameResolver)
302+
isExternal := doc.addRequestBodyToSpec(op.RequestBody, refNameResolver, parentIsExternal)
298303
if op.RequestBody != nil && op.RequestBody.Value != nil {
299-
doc.derefRequestBody(*op.RequestBody.Value, refNameResolver)
304+
doc.derefRequestBody(*op.RequestBody.Value, refNameResolver, parentIsExternal || isExternal)
300305
}
301306
for _, cb := range op.Callbacks {
302-
doc.addCallbackToSpec(cb, refNameResolver)
307+
isExternal := doc.addCallbackToSpec(cb, refNameResolver, parentIsExternal)
303308
if cb.Value != nil {
304-
doc.derefPaths(*cb.Value, refNameResolver)
309+
doc.derefPaths(*cb.Value, refNameResolver, parentIsExternal || isExternal)
305310
}
306311
}
307-
doc.derefResponses(op.Responses, refNameResolver)
312+
doc.derefResponses(op.Responses, refNameResolver, parentIsExternal)
308313
for _, param := range op.Parameters {
309-
doc.addParameterToSpec(param, refNameResolver)
314+
isExternal := doc.addParameterToSpec(param, refNameResolver, parentIsExternal)
310315
if param.Value != nil {
311-
doc.derefParameter(*param.Value, refNameResolver)
316+
doc.derefParameter(*param.Value, refNameResolver, parentIsExternal || isExternal)
312317
}
313318
}
314319
}
@@ -337,42 +342,42 @@ func (doc *T) InternalizeRefs(ctx context.Context, refNameResolver func(ref stri
337342
names := schemaNames(doc.Components.Schemas)
338343
for _, name := range names {
339344
schema := doc.Components.Schemas[name]
340-
doc.addSchemaToSpec(schema, refNameResolver)
345+
isExternal := doc.addSchemaToSpec(schema, refNameResolver, false)
341346
if schema != nil {
342347
schema.Ref = "" // always dereference the top level
343-
doc.derefSchema(schema.Value, refNameResolver)
348+
doc.derefSchema(schema.Value, refNameResolver, isExternal)
344349
}
345350
}
346351
names = parametersMapNames(doc.Components.Parameters)
347352
for _, name := range names {
348353
p := doc.Components.Parameters[name]
349-
doc.addParameterToSpec(p, refNameResolver)
354+
isExternal := doc.addParameterToSpec(p, refNameResolver, false)
350355
if p != nil && p.Value != nil {
351356
p.Ref = "" // always dereference the top level
352-
doc.derefParameter(*p.Value, refNameResolver)
357+
doc.derefParameter(*p.Value, refNameResolver, isExternal)
353358
}
354359
}
355-
doc.derefHeaders(doc.Components.Headers, refNameResolver)
360+
doc.derefHeaders(doc.Components.Headers, refNameResolver, false)
356361
for _, req := range doc.Components.RequestBodies {
357-
doc.addRequestBodyToSpec(req, refNameResolver)
362+
isExternal := doc.addRequestBodyToSpec(req, refNameResolver, false)
358363
if req != nil && req.Value != nil {
359364
req.Ref = "" // always dereference the top level
360-
doc.derefRequestBody(*req.Value, refNameResolver)
365+
doc.derefRequestBody(*req.Value, refNameResolver, isExternal)
361366
}
362367
}
363-
doc.derefResponses(doc.Components.Responses, refNameResolver)
368+
doc.derefResponses(doc.Components.Responses, refNameResolver, false)
364369
for _, ss := range doc.Components.SecuritySchemes {
365-
doc.addSecuritySchemeToSpec(ss, refNameResolver)
370+
doc.addSecuritySchemeToSpec(ss, refNameResolver, false)
366371
}
367-
doc.derefExamples(doc.Components.Examples, refNameResolver)
368-
doc.derefLinks(doc.Components.Links, refNameResolver)
372+
doc.derefExamples(doc.Components.Examples, refNameResolver, false)
373+
doc.derefLinks(doc.Components.Links, refNameResolver, false)
369374
for _, cb := range doc.Components.Callbacks {
370-
doc.addCallbackToSpec(cb, refNameResolver)
375+
isExternal := doc.addCallbackToSpec(cb, refNameResolver, false)
371376
if cb != nil && cb.Value != nil {
372377
cb.Ref = "" // always dereference the top level
373-
doc.derefPaths(*cb.Value, refNameResolver)
378+
doc.derefPaths(*cb.Value, refNameResolver, isExternal)
374379
}
375380
}
376381

377-
doc.derefPaths(doc.Paths, refNameResolver)
382+
doc.derefPaths(doc.Paths, refNameResolver, false)
378383
}

0 commit comments

Comments
 (0)