1/*
2Copyright 2017 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package aggregator
18
19import (
20	_ "net/http/pprof"
21
22	"k8s.io/kube-openapi/pkg/validation/spec"
23)
24
25// Run a walkRefCallback method on all references of an OpenAPI spec, replacing the values.
26type mutatingReferenceWalker struct {
27	// walkRefCallback will be called on each reference. Do not mutate the input, always create a copy first and return that.
28	walkRefCallback func(ref *spec.Ref) *spec.Ref
29}
30
31// replaceReferences rewrites the references without mutating the input.
32// The output might share data with the input.
33func replaceReferences(walkRef func(ref *spec.Ref) *spec.Ref, sp *spec.Swagger) *spec.Swagger {
34	walker := &mutatingReferenceWalker{walkRefCallback: walkRef}
35	return walker.Start(sp)
36}
37
38func (w *mutatingReferenceWalker) walkSchema(schema *spec.Schema) *spec.Schema {
39	if schema == nil {
40		return nil
41	}
42
43	orig := schema
44	clone := func() {
45		if orig == schema {
46			schema = &spec.Schema{}
47			*schema = *orig
48		}
49	}
50
51	if r := w.walkRefCallback(&schema.Ref); r != &schema.Ref {
52		clone()
53		schema.Ref = *r
54	}
55
56	definitionsCloned := false
57	for k, v := range schema.Definitions {
58		if s := w.walkSchema(&v); s != &v {
59			if !definitionsCloned {
60				definitionsCloned = true
61				clone()
62				schema.Definitions = make(spec.Definitions, len(orig.Definitions))
63				for k2, v2 := range orig.Definitions {
64					schema.Definitions[k2] = v2
65				}
66			}
67			schema.Definitions[k] = *s
68		}
69	}
70
71	propertiesCloned := false
72	for k, v := range schema.Properties {
73		if s := w.walkSchema(&v); s != &v {
74			if !propertiesCloned {
75				propertiesCloned = true
76				clone()
77				schema.Properties = make(map[string]spec.Schema, len(orig.Properties))
78				for k2, v2 := range orig.Properties {
79					schema.Properties[k2] = v2
80				}
81			}
82			schema.Properties[k] = *s
83		}
84	}
85
86	patternPropertiesCloned := false
87	for k, v := range schema.PatternProperties {
88		if s := w.walkSchema(&v); s != &v {
89			if !patternPropertiesCloned {
90				patternPropertiesCloned = true
91				clone()
92				schema.PatternProperties = make(map[string]spec.Schema, len(orig.PatternProperties))
93				for k2, v2 := range orig.PatternProperties {
94					schema.PatternProperties[k2] = v2
95				}
96			}
97			schema.PatternProperties[k] = *s
98		}
99	}
100
101	allOfCloned := false
102	for i := range schema.AllOf {
103		if s := w.walkSchema(&schema.AllOf[i]); s != &schema.AllOf[i] {
104			if !allOfCloned {
105				allOfCloned = true
106				clone()
107				schema.AllOf = make([]spec.Schema, len(orig.AllOf))
108				copy(schema.AllOf, orig.AllOf)
109			}
110			schema.AllOf[i] = *s
111		}
112	}
113
114	anyOfCloned := false
115	for i := range schema.AnyOf {
116		if s := w.walkSchema(&schema.AnyOf[i]); s != &schema.AnyOf[i] {
117			if !anyOfCloned {
118				anyOfCloned = true
119				clone()
120				schema.AnyOf = make([]spec.Schema, len(orig.AnyOf))
121				copy(schema.AnyOf, orig.AnyOf)
122			}
123			schema.AnyOf[i] = *s
124		}
125	}
126
127	oneOfCloned := false
128	for i := range schema.OneOf {
129		if s := w.walkSchema(&schema.OneOf[i]); s != &schema.OneOf[i] {
130			if !oneOfCloned {
131				oneOfCloned = true
132				clone()
133				schema.OneOf = make([]spec.Schema, len(orig.OneOf))
134				copy(schema.OneOf, orig.OneOf)
135			}
136			schema.OneOf[i] = *s
137		}
138	}
139
140	if schema.Not != nil {
141		if s := w.walkSchema(schema.Not); s != schema.Not {
142			clone()
143			schema.Not = s
144		}
145	}
146
147	if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
148		if s := w.walkSchema(schema.AdditionalProperties.Schema); s != schema.AdditionalProperties.Schema {
149			clone()
150			schema.AdditionalProperties = &spec.SchemaOrBool{Schema: s, Allows: schema.AdditionalProperties.Allows}
151		}
152	}
153
154	if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil {
155		if s := w.walkSchema(schema.AdditionalItems.Schema); s != schema.AdditionalItems.Schema {
156			clone()
157			schema.AdditionalItems = &spec.SchemaOrBool{Schema: s, Allows: schema.AdditionalItems.Allows}
158		}
159	}
160
161	if schema.Items != nil {
162		if schema.Items.Schema != nil {
163			if s := w.walkSchema(schema.Items.Schema); s != schema.Items.Schema {
164				clone()
165				schema.Items = &spec.SchemaOrArray{Schema: s}
166			}
167		} else {
168			itemsCloned := false
169			for i := range schema.Items.Schemas {
170				if s := w.walkSchema(&schema.Items.Schemas[i]); s != &schema.Items.Schemas[i] {
171					if !itemsCloned {
172						clone()
173						schema.Items = &spec.SchemaOrArray{
174							Schemas: make([]spec.Schema, len(orig.Items.Schemas)),
175						}
176						itemsCloned = true
177						copy(schema.Items.Schemas, orig.Items.Schemas)
178					}
179					schema.Items.Schemas[i] = *s
180				}
181			}
182		}
183	}
184
185	return schema
186}
187
188func (w *mutatingReferenceWalker) walkParameter(param *spec.Parameter) *spec.Parameter {
189	if param == nil {
190		return nil
191	}
192
193	orig := param
194	cloned := false
195	clone := func() {
196		if !cloned {
197			cloned = true
198			param = &spec.Parameter{}
199			*param = *orig
200		}
201	}
202
203	if r := w.walkRefCallback(&param.Ref); r != &param.Ref {
204		clone()
205		param.Ref = *r
206	}
207	if s := w.walkSchema(param.Schema); s != param.Schema {
208		clone()
209		param.Schema = s
210	}
211	if param.Items != nil {
212		if r := w.walkRefCallback(&param.Items.Ref); r != &param.Items.Ref {
213			param.Items.Ref = *r
214		}
215	}
216
217	return param
218}
219
220func (w *mutatingReferenceWalker) walkParameters(params []spec.Parameter) ([]spec.Parameter, bool) {
221	if params == nil {
222		return nil, false
223	}
224
225	orig := params
226	cloned := false
227	clone := func() {
228		if !cloned {
229			cloned = true
230			params = make([]spec.Parameter, len(params))
231			copy(params, orig)
232		}
233	}
234
235	for i := range params {
236		if s := w.walkParameter(&params[i]); s != &params[i] {
237			clone()
238			params[i] = *s
239		}
240	}
241
242	return params, cloned
243}
244
245func (w *mutatingReferenceWalker) walkResponse(resp *spec.Response) *spec.Response {
246	if resp == nil {
247		return nil
248	}
249
250	orig := resp
251	cloned := false
252	clone := func() {
253		if !cloned {
254			cloned = true
255			resp = &spec.Response{}
256			*resp = *orig
257		}
258	}
259
260	if r := w.walkRefCallback(&resp.Ref); r != &resp.Ref {
261		clone()
262		resp.Ref = *r
263	}
264	if s := w.walkSchema(resp.Schema); s != resp.Schema {
265		clone()
266		resp.Schema = s
267	}
268
269	return resp
270}
271
272func (w *mutatingReferenceWalker) walkResponses(resps *spec.Responses) *spec.Responses {
273	if resps == nil {
274		return nil
275	}
276
277	orig := resps
278	cloned := false
279	clone := func() {
280		if !cloned {
281			cloned = true
282			resps = &spec.Responses{}
283			*resps = *orig
284		}
285	}
286
287	if r := w.walkResponse(resps.ResponsesProps.Default); r != resps.ResponsesProps.Default {
288		clone()
289		resps.Default = r
290	}
291
292	responsesCloned := false
293	for k, v := range resps.ResponsesProps.StatusCodeResponses {
294		if r := w.walkResponse(&v); r != &v {
295			if !responsesCloned {
296				responsesCloned = true
297				clone()
298				resps.ResponsesProps.StatusCodeResponses = make(map[int]spec.Response, len(orig.StatusCodeResponses))
299				for k2, v2 := range orig.StatusCodeResponses {
300					resps.ResponsesProps.StatusCodeResponses[k2] = v2
301				}
302			}
303			resps.ResponsesProps.StatusCodeResponses[k] = *r
304		}
305	}
306
307	return resps
308}
309
310func (w *mutatingReferenceWalker) walkOperation(op *spec.Operation) *spec.Operation {
311	if op == nil {
312		return nil
313	}
314
315	orig := op
316	cloned := false
317	clone := func() {
318		if !cloned {
319			cloned = true
320			op = &spec.Operation{}
321			*op = *orig
322		}
323	}
324
325	parametersCloned := false
326	for i := range op.Parameters {
327		if s := w.walkParameter(&op.Parameters[i]); s != &op.Parameters[i] {
328			if !parametersCloned {
329				parametersCloned = true
330				clone()
331				op.Parameters = make([]spec.Parameter, len(orig.Parameters))
332				copy(op.Parameters, orig.Parameters)
333			}
334			op.Parameters[i] = *s
335		}
336	}
337
338	if r := w.walkResponses(op.Responses); r != op.Responses {
339		clone()
340		op.Responses = r
341	}
342
343	return op
344}
345
346func (w *mutatingReferenceWalker) walkPathItem(pathItem *spec.PathItem) *spec.PathItem {
347	if pathItem == nil {
348		return nil
349	}
350
351	orig := pathItem
352	cloned := false
353	clone := func() {
354		if !cloned {
355			cloned = true
356			pathItem = &spec.PathItem{}
357			*pathItem = *orig
358		}
359	}
360
361	if p, changed := w.walkParameters(pathItem.Parameters); changed {
362		clone()
363		pathItem.Parameters = p
364	}
365	if op := w.walkOperation(pathItem.Get); op != pathItem.Get {
366		clone()
367		pathItem.Get = op
368	}
369	if op := w.walkOperation(pathItem.Head); op != pathItem.Head {
370		clone()
371		pathItem.Head = op
372	}
373	if op := w.walkOperation(pathItem.Delete); op != pathItem.Delete {
374		clone()
375		pathItem.Delete = op
376	}
377	if op := w.walkOperation(pathItem.Options); op != pathItem.Options {
378		clone()
379		pathItem.Options = op
380	}
381	if op := w.walkOperation(pathItem.Patch); op != pathItem.Patch {
382		clone()
383		pathItem.Patch = op
384	}
385	if op := w.walkOperation(pathItem.Post); op != pathItem.Post {
386		clone()
387		pathItem.Post = op
388	}
389	if op := w.walkOperation(pathItem.Put); op != pathItem.Put {
390		clone()
391		pathItem.Put = op
392	}
393
394	return pathItem
395}
396
397func (w *mutatingReferenceWalker) walkPaths(paths *spec.Paths) *spec.Paths {
398	if paths == nil {
399		return nil
400	}
401
402	orig := paths
403	cloned := false
404	clone := func() {
405		if !cloned {
406			cloned = true
407			paths = &spec.Paths{}
408			*paths = *orig
409		}
410	}
411
412	pathsCloned := false
413	for k, v := range paths.Paths {
414		if p := w.walkPathItem(&v); p != &v {
415			if !pathsCloned {
416				pathsCloned = true
417				clone()
418				paths.Paths = make(map[string]spec.PathItem, len(orig.Paths))
419				for k2, v2 := range orig.Paths {
420					paths.Paths[k2] = v2
421				}
422			}
423			paths.Paths[k] = *p
424		}
425	}
426
427	return paths
428}
429
430func (w *mutatingReferenceWalker) Start(swagger *spec.Swagger) *spec.Swagger {
431	if swagger == nil {
432		return nil
433	}
434
435	orig := swagger
436	cloned := false
437	clone := func() {
438		if !cloned {
439			cloned = true
440			swagger = &spec.Swagger{}
441			*swagger = *orig
442		}
443	}
444
445	parametersCloned := false
446	for k, v := range swagger.Parameters {
447		if p := w.walkParameter(&v); p != &v {
448			if !parametersCloned {
449				parametersCloned = true
450				clone()
451				swagger.Parameters = make(map[string]spec.Parameter, len(orig.Parameters))
452				for k2, v2 := range orig.Parameters {
453					swagger.Parameters[k2] = v2
454				}
455			}
456			swagger.Parameters[k] = *p
457		}
458	}
459
460	responsesCloned := false
461	for k, v := range swagger.Responses {
462		if r := w.walkResponse(&v); r != &v {
463			if !responsesCloned {
464				responsesCloned = true
465				clone()
466				swagger.Responses = make(map[string]spec.Response, len(orig.Responses))
467				for k2, v2 := range orig.Responses {
468					swagger.Responses[k2] = v2
469				}
470			}
471			swagger.Responses[k] = *r
472		}
473	}
474
475	definitionsCloned := false
476	for k, v := range swagger.Definitions {
477		if s := w.walkSchema(&v); s != &v {
478			if !definitionsCloned {
479				definitionsCloned = true
480				clone()
481				swagger.Definitions = make(spec.Definitions, len(orig.Definitions))
482				for k2, v2 := range orig.Definitions {
483					swagger.Definitions[k2] = v2
484				}
485			}
486			swagger.Definitions[k] = *s
487		}
488	}
489
490	if swagger.Paths != nil {
491		if p := w.walkPaths(swagger.Paths); p != swagger.Paths {
492			clone()
493			swagger.Paths = p
494		}
495	}
496
497	return swagger
498}
499