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, ¶m); err != nil { 523 return err 524 } 525 component.Value = ¶m 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