1package openapi3
2
3import (
4	"context"
5	"encoding/json"
6	"errors"
7	"fmt"
8	"io/ioutil"
9	"net/http"
10	"net/url"
11	"path"
12	"path/filepath"
13	"reflect"
14	"strconv"
15	"strings"
16
17	"github.com/ghodss/yaml"
18)
19
20func foundUnresolvedRef(ref string) error {
21	return fmt.Errorf("found unresolved ref: %q", ref)
22}
23
24func failedToResolveRefFragmentPart(value, what string) error {
25	return fmt.Errorf("failed to resolve %q in fragment in URI: %q", what, value)
26}
27
28// Loader helps deserialize an OpenAPIv3 document
29type Loader struct {
30	// IsExternalRefsAllowed enables visiting other files
31	IsExternalRefsAllowed bool
32
33	// ReadFromURIFunc allows overriding the any file/URL reading func
34	ReadFromURIFunc func(loader *Loader, url *url.URL) ([]byte, error)
35
36	Context context.Context
37
38	rootDir string
39
40	visitedPathItemRefs map[string]struct{}
41
42	visitedDocuments map[string]*T
43
44	visitedExample        map[*Example]struct{}
45	visitedHeader         map[*Header]struct{}
46	visitedLink           map[*Link]struct{}
47	visitedParameter      map[*Parameter]struct{}
48	visitedRequestBody    map[*RequestBody]struct{}
49	visitedResponse       map[*Response]struct{}
50	visitedSchema         map[*Schema]struct{}
51	visitedSecurityScheme map[*SecurityScheme]struct{}
52}
53
54// NewLoader returns an empty Loader
55func NewLoader() *Loader {
56	return &Loader{}
57}
58
59func (loader *Loader) resetVisitedPathItemRefs() {
60	loader.visitedPathItemRefs = make(map[string]struct{})
61}
62
63// LoadFromURI loads a spec from a remote URL
64func (loader *Loader) LoadFromURI(location *url.URL) (*T, error) {
65	loader.resetVisitedPathItemRefs()
66	return loader.loadFromURIInternal(location)
67}
68
69// LoadFromFile loads a spec from a local file path
70func (loader *Loader) LoadFromFile(location string) (*T, error) {
71	loader.rootDir = path.Dir(location)
72	return loader.LoadFromURI(&url.URL{Path: filepath.ToSlash(location)})
73}
74
75func (loader *Loader) loadFromURIInternal(location *url.URL) (*T, error) {
76	data, err := loader.readURL(location)
77	if err != nil {
78		return nil, err
79	}
80	return loader.loadFromDataWithPathInternal(data, location)
81}
82
83func (loader *Loader) allowsExternalRefs(ref string) (err error) {
84	if !loader.IsExternalRefsAllowed {
85		err = fmt.Errorf("encountered disallowed external reference: %q", ref)
86	}
87	return
88}
89
90// loadSingleElementFromURI reads the data from ref and unmarshals to the passed element.
91func (loader *Loader) loadSingleElementFromURI(ref string, rootPath *url.URL, element interface{}) (*url.URL, error) {
92	if err := loader.allowsExternalRefs(ref); err != nil {
93		return nil, err
94	}
95
96	parsedURL, err := url.Parse(ref)
97	if err != nil {
98		return nil, err
99	}
100	if fragment := parsedURL.Fragment; fragment != "" {
101		return nil, fmt.Errorf("unexpected ref fragment %q", fragment)
102	}
103
104	resolvedPath, err := resolvePath(rootPath, parsedURL)
105	if err != nil {
106		return nil, fmt.Errorf("could not resolve path: %v", err)
107	}
108
109	data, err := loader.readURL(resolvedPath)
110	if err != nil {
111		return nil, err
112	}
113	if err := yaml.Unmarshal(data, element); err != nil {
114		return nil, err
115	}
116
117	return resolvedPath, nil
118}
119
120func (loader *Loader) readURL(location *url.URL) ([]byte, error) {
121	if f := loader.ReadFromURIFunc; f != nil {
122		return f(loader, location)
123	}
124
125	if location.Scheme != "" && location.Host != "" {
126		resp, err := http.Get(location.String())
127		if err != nil {
128			return nil, err
129		}
130		defer resp.Body.Close()
131		if resp.StatusCode > 399 {
132			return nil, fmt.Errorf("error loading %q: request returned status code %d", location.String(), resp.StatusCode)
133		}
134		return ioutil.ReadAll(resp.Body)
135	}
136	if location.Scheme != "" || location.Host != "" || location.RawQuery != "" {
137		return nil, fmt.Errorf("unsupported URI: %q", location.String())
138	}
139	return ioutil.ReadFile(location.Path)
140}
141
142// LoadFromData loads a spec from a byte array
143func (loader *Loader) LoadFromData(data []byte) (*T, error) {
144	loader.resetVisitedPathItemRefs()
145	doc := &T{}
146	if err := yaml.Unmarshal(data, doc); err != nil {
147		return nil, err
148	}
149	if err := loader.ResolveRefsIn(doc, nil); err != nil {
150		return nil, err
151	}
152	return doc, nil
153}
154
155// LoadFromDataWithPath takes the OpenAPI document data in bytes and a path where the resolver can find referred
156// elements and returns a *T with all resolved data or an error if unable to load data or resolve refs.
157func (loader *Loader) LoadFromDataWithPath(data []byte, location *url.URL) (*T, error) {
158	loader.resetVisitedPathItemRefs()
159	return loader.loadFromDataWithPathInternal(data, location)
160}
161
162func (loader *Loader) loadFromDataWithPathInternal(data []byte, location *url.URL) (*T, error) {
163	if loader.visitedDocuments == nil {
164		loader.visitedDocuments = make(map[string]*T)
165	}
166	uri := location.String()
167	if doc, ok := loader.visitedDocuments[uri]; ok {
168		return doc, nil
169	}
170
171	doc := &T{}
172	loader.visitedDocuments[uri] = doc
173
174	if err := yaml.Unmarshal(data, doc); err != nil {
175		return nil, err
176	}
177	if err := loader.ResolveRefsIn(doc, location); err != nil {
178		return nil, err
179	}
180
181	return doc, nil
182}
183
184// ResolveRefsIn expands references if for instance spec was just unmarshalled
185func (loader *Loader) ResolveRefsIn(doc *T, location *url.URL) (err error) {
186	if loader.visitedPathItemRefs == nil {
187		loader.resetVisitedPathItemRefs()
188	}
189
190	// Visit all components
191	components := doc.Components
192	for _, component := range components.Headers {
193		if err = loader.resolveHeaderRef(doc, component, location); err != nil {
194			return
195		}
196	}
197	for _, component := range components.Parameters {
198		if err = loader.resolveParameterRef(doc, component, location); err != nil {
199			return
200		}
201	}
202	for _, component := range components.RequestBodies {
203		if err = loader.resolveRequestBodyRef(doc, component, location); err != nil {
204			return
205		}
206	}
207	for _, component := range components.Responses {
208		if err = loader.resolveResponseRef(doc, component, location); err != nil {
209			return
210		}
211	}
212	for _, component := range components.Schemas {
213		if err = loader.resolveSchemaRef(doc, component, location); err != nil {
214			return
215		}
216	}
217	for _, component := range components.SecuritySchemes {
218		if err = loader.resolveSecuritySchemeRef(doc, component, location); err != nil {
219			return
220		}
221	}
222	for _, component := range components.Examples {
223		if err = loader.resolveExampleRef(doc, component, location); err != nil {
224			return
225		}
226	}
227	for _, component := range components.Callbacks {
228		if err = loader.resolveCallbackRef(doc, component, location); err != nil {
229			return
230		}
231	}
232
233	// Visit all operations
234	for entrypoint, pathItem := range doc.Paths {
235		if pathItem == nil {
236			continue
237		}
238		if err = loader.resolvePathItemRef(doc, entrypoint, pathItem, location); err != nil {
239			return
240		}
241	}
242
243	return
244}
245
246func join(basePath *url.URL, relativePath *url.URL) (*url.URL, error) {
247	if basePath == nil {
248		return relativePath, nil
249	}
250	newPath, err := url.Parse(basePath.String())
251	if err != nil {
252		return nil, fmt.Errorf("cannot copy path: %q", basePath.String())
253	}
254	newPath.Path = path.Join(path.Dir(newPath.Path), relativePath.Path)
255	return newPath, nil
256}
257
258func resolvePath(basePath *url.URL, componentPath *url.URL) (*url.URL, error) {
259	if componentPath.Scheme == "" && componentPath.Host == "" {
260		// support absolute paths
261		if componentPath.Path[0] == '/' {
262			return componentPath, nil
263		}
264		return join(basePath, componentPath)
265	}
266	return componentPath, nil
267}
268
269func isSingleRefElement(ref string) bool {
270	return !strings.Contains(ref, "#")
271}
272
273func (loader *Loader) resolveComponent(
274	doc *T,
275	ref string,
276	path *url.URL,
277	resolved interface{},
278) (
279	componentPath *url.URL,
280	err error,
281) {
282	if doc, ref, componentPath, err = loader.resolveRef(doc, ref, path); err != nil {
283		return nil, err
284	}
285
286	parsedURL, err := url.Parse(ref)
287	if err != nil {
288		return nil, fmt.Errorf("cannot parse reference: %q: %v", ref, parsedURL)
289	}
290	fragment := parsedURL.Fragment
291	if !strings.HasPrefix(fragment, "/") {
292		return nil, fmt.Errorf("expected fragment prefix '#/' in URI %q", ref)
293	}
294
295	drill := func(cursor interface{}) (interface{}, error) {
296		for _, pathPart := range strings.Split(fragment[1:], "/") {
297			pathPart = unescapeRefString(pathPart)
298
299			if cursor, err = drillIntoField(cursor, pathPart); err != nil {
300				e := failedToResolveRefFragmentPart(ref, pathPart)
301				return nil, fmt.Errorf("%s: %s", e.Error(), err.Error())
302			}
303			if cursor == nil {
304				return nil, failedToResolveRefFragmentPart(ref, pathPart)
305			}
306		}
307		return cursor, nil
308	}
309	var cursor interface{}
310	if cursor, err = drill(doc); err != nil {
311		if path == nil {
312			return nil, err
313		}
314		var err2 error
315		data, err2 := loader.readURL(path)
316		if err2 != nil {
317			return nil, err
318		}
319		if err2 = yaml.Unmarshal(data, &cursor); err2 != nil {
320			return nil, err
321		}
322		if cursor, err2 = drill(cursor); err2 != nil || cursor == nil {
323			return nil, err
324		}
325		err = nil
326	}
327
328	switch {
329	case reflect.TypeOf(cursor) == reflect.TypeOf(resolved):
330		reflect.ValueOf(resolved).Elem().Set(reflect.ValueOf(cursor).Elem())
331		return componentPath, nil
332
333	case reflect.TypeOf(cursor) == reflect.TypeOf(map[string]interface{}{}):
334		codec := func(got, expect interface{}) error {
335			enc, err := json.Marshal(got)
336			if err != nil {
337				return err
338			}
339			if err = json.Unmarshal(enc, expect); err != nil {
340				return err
341			}
342			return nil
343		}
344		if err := codec(cursor, resolved); err != nil {
345			return nil, fmt.Errorf("bad data in %q", ref)
346		}
347		return componentPath, nil
348
349	default:
350		return nil, fmt.Errorf("bad data in %q", ref)
351	}
352}
353
354func drillIntoField(cursor interface{}, fieldName string) (interface{}, error) {
355	// Special case due to multijson
356	if s, ok := cursor.(*SchemaRef); ok && fieldName == "additionalProperties" {
357		if ap := s.Value.AdditionalPropertiesAllowed; ap != nil {
358			return *ap, nil
359		}
360		return s.Value.AdditionalProperties, nil
361	}
362
363	switch val := reflect.Indirect(reflect.ValueOf(cursor)); val.Kind() {
364	case reflect.Map:
365		elementValue := val.MapIndex(reflect.ValueOf(fieldName))
366		if !elementValue.IsValid() {
367			return nil, fmt.Errorf("map key %q not found", fieldName)
368		}
369		return elementValue.Interface(), nil
370
371	case reflect.Slice:
372		i, err := strconv.ParseUint(fieldName, 10, 32)
373		if err != nil {
374			return nil, err
375		}
376		index := int(i)
377		if 0 > index || index >= val.Len() {
378			return nil, errors.New("slice index out of bounds")
379		}
380		return val.Index(index).Interface(), nil
381
382	case reflect.Struct:
383		hasFields := false
384		for i := 0; i < val.NumField(); i++ {
385			hasFields = true
386			field := val.Type().Field(i)
387			tagValue := field.Tag.Get("yaml")
388			yamlKey := strings.Split(tagValue, ",")[0]
389			if yamlKey == "-" {
390				tagValue := field.Tag.Get("multijson")
391				yamlKey = strings.Split(tagValue, ",")[0]
392			}
393			if yamlKey == fieldName {
394				return val.Field(i).Interface(), nil
395			}
396		}
397		// if cursor is a "ref wrapper" struct (e.g. RequestBodyRef),
398		if _, ok := val.Type().FieldByName("Value"); ok {
399			// try digging into its Value field
400			return drillIntoField(val.FieldByName("Value").Interface(), fieldName)
401		}
402		if hasFields {
403			if ff := val.Type().Field(0); ff.PkgPath == "" && ff.Name == "ExtensionProps" {
404				extensions := val.Field(0).Interface().(ExtensionProps).Extensions
405				if enc, ok := extensions[fieldName]; ok {
406					var dec interface{}
407					if err := json.Unmarshal(enc.(json.RawMessage), &dec); err != nil {
408						return nil, err
409					}
410					return dec, nil
411				}
412			}
413		}
414		return nil, fmt.Errorf("struct field %q not found", fieldName)
415
416	default:
417		return nil, errors.New("not a map, slice nor struct")
418	}
419}
420
421func (loader *Loader) documentPathForRecursiveRef(current *url.URL, resolvedRef string) *url.URL {
422	if loader.rootDir == "" {
423		return current
424	}
425	return &url.URL{Path: path.Join(loader.rootDir, resolvedRef)}
426
427}
428
429func (loader *Loader) resolveRef(doc *T, ref string, path *url.URL) (*T, string, *url.URL, error) {
430	if ref != "" && ref[0] == '#' {
431		return doc, ref, path, nil
432	}
433
434	if err := loader.allowsExternalRefs(ref); err != nil {
435		return nil, "", nil, err
436	}
437
438	parsedURL, err := url.Parse(ref)
439	if err != nil {
440		return nil, "", nil, fmt.Errorf("cannot parse reference: %q: %v", ref, parsedURL)
441	}
442	fragment := parsedURL.Fragment
443	parsedURL.Fragment = ""
444
445	var resolvedPath *url.URL
446	if resolvedPath, err = resolvePath(path, parsedURL); err != nil {
447		return nil, "", nil, fmt.Errorf("error resolving path: %v", err)
448	}
449
450	if doc, err = loader.loadFromURIInternal(resolvedPath); err != nil {
451		return nil, "", nil, fmt.Errorf("error resolving reference %q: %v", ref, err)
452	}
453
454	return doc, "#" + fragment, resolvedPath, nil
455}
456
457func (loader *Loader) resolveHeaderRef(doc *T, component *HeaderRef, documentPath *url.URL) (err error) {
458	if component != nil && component.Value != nil {
459		if loader.visitedHeader == nil {
460			loader.visitedHeader = make(map[*Header]struct{})
461		}
462		if _, ok := loader.visitedHeader[component.Value]; ok {
463			return nil
464		}
465		loader.visitedHeader[component.Value] = struct{}{}
466	}
467
468	if component == nil {
469		return errors.New("invalid header: value MUST be an object")
470	}
471	if ref := component.Ref; ref != "" {
472		if isSingleRefElement(ref) {
473			var header Header
474			if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &header); err != nil {
475				return err
476			}
477			component.Value = &header
478		} else {
479			var resolved HeaderRef
480			componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved)
481			if err != nil {
482				return err
483			}
484			if err := loader.resolveHeaderRef(doc, &resolved, componentPath); err != nil {
485				return err
486			}
487			component.Value = resolved.Value
488			documentPath = loader.documentPathForRecursiveRef(documentPath, resolved.Ref)
489		}
490	}
491	value := component.Value
492	if value == nil {
493		return nil
494	}
495
496	if schema := value.Schema; schema != nil {
497		if err := loader.resolveSchemaRef(doc, schema, documentPath); err != nil {
498			return err
499		}
500	}
501	return nil
502}
503
504func (loader *Loader) resolveParameterRef(doc *T, component *ParameterRef, documentPath *url.URL) (err error) {
505	if component != nil && component.Value != nil {
506		if loader.visitedParameter == nil {
507			loader.visitedParameter = make(map[*Parameter]struct{})
508		}
509		if _, ok := loader.visitedParameter[component.Value]; ok {
510			return nil
511		}
512		loader.visitedParameter[component.Value] = struct{}{}
513	}
514
515	if component == nil {
516		return errors.New("invalid parameter: value MUST be an object")
517	}
518	ref := component.Ref
519	if ref != "" {
520		if isSingleRefElement(ref) {
521			var param Parameter
522			if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &param); err != nil {
523				return err
524			}
525			component.Value = &param
526		} else {
527			var resolved ParameterRef
528			componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved)
529			if err != nil {
530				return err
531			}
532			if err := loader.resolveParameterRef(doc, &resolved, componentPath); err != nil {
533				return err
534			}
535			component.Value = resolved.Value
536			documentPath = loader.documentPathForRecursiveRef(documentPath, resolved.Ref)
537		}
538	}
539	value := component.Value
540	if value == nil {
541		return nil
542	}
543
544	if value.Content != nil && value.Schema != nil {
545		return errors.New("cannot contain both schema and content in a parameter")
546	}
547	for _, contentType := range value.Content {
548		if schema := contentType.Schema; schema != nil {
549			if err := loader.resolveSchemaRef(doc, schema, documentPath); err != nil {
550				return err
551			}
552		}
553	}
554	if schema := value.Schema; schema != nil {
555		if err := loader.resolveSchemaRef(doc, schema, documentPath); err != nil {
556			return err
557		}
558	}
559	return nil
560}
561
562func (loader *Loader) resolveRequestBodyRef(doc *T, component *RequestBodyRef, documentPath *url.URL) (err error) {
563	if component != nil && component.Value != nil {
564		if loader.visitedRequestBody == nil {
565			loader.visitedRequestBody = make(map[*RequestBody]struct{})
566		}
567		if _, ok := loader.visitedRequestBody[component.Value]; ok {
568			return nil
569		}
570		loader.visitedRequestBody[component.Value] = struct{}{}
571	}
572
573	if component == nil {
574		return errors.New("invalid requestBody: value MUST be an object")
575	}
576	if ref := component.Ref; ref != "" {
577		if isSingleRefElement(ref) {
578			var requestBody RequestBody
579			if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &requestBody); err != nil {
580				return err
581			}
582			component.Value = &requestBody
583		} else {
584			var resolved RequestBodyRef
585			componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved)
586			if err != nil {
587				return err
588			}
589			if err = loader.resolveRequestBodyRef(doc, &resolved, componentPath); err != nil {
590				return err
591			}
592			component.Value = resolved.Value
593			documentPath = loader.documentPathForRecursiveRef(documentPath, resolved.Ref)
594		}
595	}
596	value := component.Value
597	if value == nil {
598		return nil
599	}
600
601	for _, contentType := range value.Content {
602		for name, example := range contentType.Examples {
603			if err := loader.resolveExampleRef(doc, example, documentPath); err != nil {
604				return err
605			}
606			contentType.Examples[name] = example
607		}
608		if schema := contentType.Schema; schema != nil {
609			if err := loader.resolveSchemaRef(doc, schema, documentPath); err != nil {
610				return err
611			}
612		}
613	}
614	return nil
615}
616
617func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documentPath *url.URL) (err error) {
618	if component != nil && component.Value != nil {
619		if loader.visitedResponse == nil {
620			loader.visitedResponse = make(map[*Response]struct{})
621		}
622		if _, ok := loader.visitedResponse[component.Value]; ok {
623			return nil
624		}
625		loader.visitedResponse[component.Value] = struct{}{}
626	}
627
628	if component == nil {
629		return errors.New("invalid response: value MUST be an object")
630	}
631	ref := component.Ref
632	if ref != "" {
633		if isSingleRefElement(ref) {
634			var resp Response
635			if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &resp); err != nil {
636				return err
637			}
638			component.Value = &resp
639		} else {
640			var resolved ResponseRef
641			componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved)
642			if err != nil {
643				return err
644			}
645			if err := loader.resolveResponseRef(doc, &resolved, componentPath); err != nil {
646				return err
647			}
648			component.Value = resolved.Value
649			documentPath = loader.documentPathForRecursiveRef(documentPath, resolved.Ref)
650		}
651	}
652	value := component.Value
653	if value == nil {
654		return nil
655	}
656
657	for _, header := range value.Headers {
658		if err := loader.resolveHeaderRef(doc, header, documentPath); err != nil {
659			return err
660		}
661	}
662	for _, contentType := range value.Content {
663		if contentType == nil {
664			continue
665		}
666		for name, example := range contentType.Examples {
667			if err := loader.resolveExampleRef(doc, example, documentPath); err != nil {
668				return err
669			}
670			contentType.Examples[name] = example
671		}
672		if schema := contentType.Schema; schema != nil {
673			if err := loader.resolveSchemaRef(doc, schema, documentPath); err != nil {
674				return err
675			}
676			contentType.Schema = schema
677		}
678	}
679	for _, link := range value.Links {
680		if err := loader.resolveLinkRef(doc, link, documentPath); err != nil {
681			return err
682		}
683	}
684	return nil
685}
686
687func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPath *url.URL) (err error) {
688	if component != nil && component.Value != nil {
689		if loader.visitedSchema == nil {
690			loader.visitedSchema = make(map[*Schema]struct{})
691		}
692		if _, ok := loader.visitedSchema[component.Value]; ok {
693			return nil
694		}
695		loader.visitedSchema[component.Value] = struct{}{}
696	}
697
698	if component == nil {
699		return errors.New("invalid schema: value MUST be an object")
700	}
701	ref := component.Ref
702	if ref != "" {
703		if isSingleRefElement(ref) {
704			var schema Schema
705			if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &schema); err != nil {
706				return err
707			}
708			component.Value = &schema
709		} else {
710			var resolved SchemaRef
711			componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved)
712			if err != nil {
713				return err
714			}
715			if err := loader.resolveSchemaRef(doc, &resolved, componentPath); err != nil {
716				return err
717			}
718			component.Value = resolved.Value
719			documentPath = loader.documentPathForRecursiveRef(documentPath, resolved.Ref)
720		}
721	}
722	value := component.Value
723	if value == nil {
724		return nil
725	}
726
727	// ResolveRefs referred schemas
728	if v := value.Items; v != nil {
729		if err := loader.resolveSchemaRef(doc, v, documentPath); err != nil {
730			return err
731		}
732	}
733	for _, v := range value.Properties {
734		if err := loader.resolveSchemaRef(doc, v, documentPath); err != nil {
735			return err
736		}
737	}
738	if v := value.AdditionalProperties; v != nil {
739		if err := loader.resolveSchemaRef(doc, v, documentPath); err != nil {
740			return err
741		}
742	}
743	if v := value.Not; v != nil {
744		if err := loader.resolveSchemaRef(doc, v, documentPath); err != nil {
745			return err
746		}
747	}
748	for _, v := range value.AllOf {
749		if err := loader.resolveSchemaRef(doc, v, documentPath); err != nil {
750			return err
751		}
752	}
753	for _, v := range value.AnyOf {
754		if err := loader.resolveSchemaRef(doc, v, documentPath); err != nil {
755			return err
756		}
757	}
758	for _, v := range value.OneOf {
759		if err := loader.resolveSchemaRef(doc, v, documentPath); err != nil {
760			return err
761		}
762	}
763	return nil
764}
765
766func (loader *Loader) resolveSecuritySchemeRef(doc *T, component *SecuritySchemeRef, documentPath *url.URL) (err error) {
767	if component != nil && component.Value != nil {
768		if loader.visitedSecurityScheme == nil {
769			loader.visitedSecurityScheme = make(map[*SecurityScheme]struct{})
770		}
771		if _, ok := loader.visitedSecurityScheme[component.Value]; ok {
772			return nil
773		}
774		loader.visitedSecurityScheme[component.Value] = struct{}{}
775	}
776
777	if component == nil {
778		return errors.New("invalid securityScheme: value MUST be an object")
779	}
780	if ref := component.Ref; ref != "" {
781		if isSingleRefElement(ref) {
782			var scheme SecurityScheme
783			if _, err = loader.loadSingleElementFromURI(ref, documentPath, &scheme); err != nil {
784				return err
785			}
786			component.Value = &scheme
787		} else {
788			var resolved SecuritySchemeRef
789			componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved)
790			if err != nil {
791				return err
792			}
793			if err := loader.resolveSecuritySchemeRef(doc, &resolved, componentPath); err != nil {
794				return err
795			}
796			component.Value = resolved.Value
797			_ = loader.documentPathForRecursiveRef(documentPath, resolved.Ref)
798		}
799	}
800	return nil
801}
802
803func (loader *Loader) resolveExampleRef(doc *T, component *ExampleRef, documentPath *url.URL) (err error) {
804	if component != nil && component.Value != nil {
805		if loader.visitedExample == nil {
806			loader.visitedExample = make(map[*Example]struct{})
807		}
808		if _, ok := loader.visitedExample[component.Value]; ok {
809			return nil
810		}
811		loader.visitedExample[component.Value] = struct{}{}
812	}
813
814	if component == nil {
815		return errors.New("invalid example: value MUST be an object")
816	}
817	if ref := component.Ref; ref != "" {
818		if isSingleRefElement(ref) {
819			var example Example
820			if _, err = loader.loadSingleElementFromURI(ref, documentPath, &example); err != nil {
821				return err
822			}
823			component.Value = &example
824		} else {
825			var resolved ExampleRef
826			componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved)
827			if err != nil {
828				return err
829			}
830			if err := loader.resolveExampleRef(doc, &resolved, componentPath); err != nil {
831				return err
832			}
833			component.Value = resolved.Value
834			_ = loader.documentPathForRecursiveRef(documentPath, resolved.Ref)
835		}
836	}
837	return nil
838}
839
840func (loader *Loader) resolveCallbackRef(doc *T, component *CallbackRef, documentPath *url.URL) (err error) {
841
842	if component == nil {
843		return errors.New("invalid callback: value MUST be an object")
844	}
845	if ref := component.Ref; ref != "" {
846		if isSingleRefElement(ref) {
847			var resolved Callback
848			if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &resolved); err != nil {
849				return err
850			}
851			component.Value = &resolved
852		} else {
853			var resolved CallbackRef
854			componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved)
855			if err != nil {
856				return err
857			}
858			if err := loader.resolveCallbackRef(doc, &resolved, componentPath); err != nil {
859				return err
860			}
861			component.Value = resolved.Value
862			documentPath = loader.documentPathForRecursiveRef(documentPath, resolved.Ref)
863		}
864	}
865	value := component.Value
866	if value == nil {
867		return nil
868	}
869
870	for entrypoint, pathItem := range *value {
871		entrypoint, pathItem := entrypoint, pathItem
872		err = func() (err error) {
873			key := "-"
874			if documentPath != nil {
875				key = documentPath.EscapedPath()
876			}
877			key += entrypoint
878			if _, ok := loader.visitedPathItemRefs[key]; ok {
879				return nil
880			}
881			loader.visitedPathItemRefs[key] = struct{}{}
882
883			if pathItem == nil {
884				return errors.New("invalid path item: value MUST be an object")
885			}
886			ref := pathItem.Ref
887			if ref != "" {
888				if isSingleRefElement(ref) {
889					var p PathItem
890					if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &p); err != nil {
891						return err
892					}
893					*pathItem = p
894				} else {
895					if doc, ref, documentPath, err = loader.resolveRef(doc, ref, documentPath); err != nil {
896						return
897					}
898
899					rest := strings.TrimPrefix(ref, "#/components/callbacks/")
900					if rest == ref {
901						return fmt.Errorf(`expected prefix "#/components/callbacks/" in URI %q`, ref)
902					}
903					id := unescapeRefString(rest)
904
905					definitions := doc.Components.Callbacks
906					if definitions == nil {
907						return failedToResolveRefFragmentPart(ref, "callbacks")
908					}
909					resolved := definitions[id]
910					if resolved == nil {
911						return failedToResolveRefFragmentPart(ref, id)
912					}
913
914					for _, p := range *resolved.Value {
915						*pathItem = *p
916						break
917					}
918				}
919			}
920			return loader.resolvePathItemRefContinued(doc, pathItem, documentPath)
921		}()
922		if err != nil {
923			return err
924		}
925	}
926	return nil
927}
928
929func (loader *Loader) resolveLinkRef(doc *T, component *LinkRef, documentPath *url.URL) (err error) {
930	if component != nil && component.Value != nil {
931		if loader.visitedLink == nil {
932			loader.visitedLink = make(map[*Link]struct{})
933		}
934		if _, ok := loader.visitedLink[component.Value]; ok {
935			return nil
936		}
937		loader.visitedLink[component.Value] = struct{}{}
938	}
939
940	if component == nil {
941		return errors.New("invalid link: value MUST be an object")
942	}
943	if ref := component.Ref; ref != "" {
944		if isSingleRefElement(ref) {
945			var link Link
946			if _, err = loader.loadSingleElementFromURI(ref, documentPath, &link); err != nil {
947				return err
948			}
949			component.Value = &link
950		} else {
951			var resolved LinkRef
952			componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved)
953			if err != nil {
954				return err
955			}
956			if err := loader.resolveLinkRef(doc, &resolved, componentPath); err != nil {
957				return err
958			}
959			component.Value = resolved.Value
960			_ = loader.documentPathForRecursiveRef(documentPath, resolved.Ref)
961		}
962	}
963	return nil
964}
965
966func (loader *Loader) resolvePathItemRef(doc *T, entrypoint string, pathItem *PathItem, documentPath *url.URL) (err error) {
967	key := "_"
968	if documentPath != nil {
969		key = documentPath.EscapedPath()
970	}
971	key += entrypoint
972	if _, ok := loader.visitedPathItemRefs[key]; ok {
973		return nil
974	}
975	loader.visitedPathItemRefs[key] = struct{}{}
976
977	if pathItem == nil {
978		return errors.New("invalid path item: value MUST be an object")
979	}
980	ref := pathItem.Ref
981	if ref != "" {
982		if isSingleRefElement(ref) {
983			var p PathItem
984			if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &p); err != nil {
985				return err
986			}
987			*pathItem = p
988		} else {
989			if doc, ref, documentPath, err = loader.resolveRef(doc, ref, documentPath); err != nil {
990				return
991			}
992
993			rest := strings.TrimPrefix(ref, "#/paths/")
994			if rest == ref {
995				return fmt.Errorf(`expected prefix "#/paths/" in URI %q`, ref)
996			}
997			id := unescapeRefString(rest)
998
999			definitions := doc.Paths
1000			if definitions == nil {
1001				return failedToResolveRefFragmentPart(ref, "paths")
1002			}
1003			resolved := definitions[id]
1004			if resolved == nil {
1005				return failedToResolveRefFragmentPart(ref, id)
1006			}
1007
1008			*pathItem = *resolved
1009		}
1010	}
1011	return loader.resolvePathItemRefContinued(doc, pathItem, documentPath)
1012}
1013
1014func (loader *Loader) resolvePathItemRefContinued(doc *T, pathItem *PathItem, documentPath *url.URL) (err error) {
1015	for _, parameter := range pathItem.Parameters {
1016		if err = loader.resolveParameterRef(doc, parameter, documentPath); err != nil {
1017			return
1018		}
1019	}
1020	for _, operation := range pathItem.Operations() {
1021		for _, parameter := range operation.Parameters {
1022			if err = loader.resolveParameterRef(doc, parameter, documentPath); err != nil {
1023				return
1024			}
1025		}
1026		if requestBody := operation.RequestBody; requestBody != nil {
1027			if err = loader.resolveRequestBodyRef(doc, requestBody, documentPath); err != nil {
1028				return
1029			}
1030		}
1031		for _, response := range operation.Responses {
1032			if err = loader.resolveResponseRef(doc, response, documentPath); err != nil {
1033				return
1034			}
1035		}
1036		for _, callback := range operation.Callbacks {
1037			if err = loader.resolveCallbackRef(doc, callback, documentPath); err != nil {
1038				return
1039			}
1040		}
1041	}
1042	return
1043}
1044
1045func unescapeRefString(ref string) string {
1046	return strings.Replace(strings.Replace(ref, "~1", "/", -1), "~0", "~", -1)
1047}
1048