github.com/snyk/vervet/v3@v3.7.0/include_headers.go (about) 1 package vervet 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "path/filepath" 7 8 "github.com/getkin/kin-openapi/openapi3" 9 ) 10 11 const ( 12 // ExtSnykIncludeHeaders is used to annotate a response with a list of 13 // headers. While OpenAPI supports header references, it does not yet 14 // support including a collection of common headers. This extension is used 15 // by vervet to include headers from a referenced document when compiling 16 // OpenAPI specs. 17 ExtSnykIncludeHeaders = "x-snyk-include-headers" 18 ) 19 20 // IncludeHeaders adds response headers included with the ExtSnykIncludeHeaders 21 // extension property. 22 func IncludeHeaders(doc *Document) error { 23 w := &includeHeaders{doc: doc} 24 err := w.apply() 25 if err != nil { 26 return err 27 } 28 return doc.ResolveRefs() 29 } 30 31 type includeHeaders struct { 32 doc *Document 33 } 34 35 func (w *includeHeaders) apply() error { 36 for _, pathItem := range w.doc.Paths { 37 if err := w.applyOperation(pathItem.Connect); err != nil { 38 return err 39 } 40 if err := w.applyOperation(pathItem.Delete); err != nil { 41 return err 42 } 43 if err := w.applyOperation(pathItem.Get); err != nil { 44 return err 45 } 46 if err := w.applyOperation(pathItem.Head); err != nil { 47 return err 48 } 49 if err := w.applyOperation(pathItem.Options); err != nil { 50 return err 51 } 52 if err := w.applyOperation(pathItem.Patch); err != nil { 53 return err 54 } 55 if err := w.applyOperation(pathItem.Post); err != nil { 56 return err 57 } 58 if err := w.applyOperation(pathItem.Put); err != nil { 59 return err 60 } 61 } 62 return nil 63 } 64 65 type includeHeadersRef struct { 66 Ref string `json:"$ref"` 67 Value openapi3.Headers `json:"-"` 68 } 69 70 func (w *includeHeaders) applyOperation(op *openapi3.Operation) error { 71 if op == nil { 72 return nil // nothing to do 73 } 74 for _, respRef := range op.Responses { 75 resp := respRef.Value 76 headersRefJson := resp.ExtensionProps.Extensions[ExtSnykIncludeHeaders] 77 if headersRefJson == nil { 78 continue 79 } 80 inclRef := &includeHeadersRef{Value: openapi3.Headers{}} 81 err := json.Unmarshal(headersRefJson.(json.RawMessage), &inclRef) 82 if err != nil { 83 return err 84 } 85 relPath, err := w.doc.LoadReference(w.doc.RelativePath(), inclRef.Ref, &inclRef.Value) 86 if err != nil { 87 return fmt.Errorf("failed to load reference: %w", err) 88 } 89 90 if resp.Headers == nil { 91 resp.Headers = openapi3.Headers{} 92 } 93 for headerKey, headerRef := range inclRef.Value { 94 if _, ok := resp.Headers[headerKey]; ok { 95 continue // Response's declared headers take precedence over includes. 96 } 97 headerRef.Ref = filepath.Join(relPath, headerRef.Ref) 98 resp.Headers[headerKey] = headerRef 99 } 100 // Remove the extension once it has been processed 101 delete(resp.ExtensionProps.Extensions, ExtSnykIncludeHeaders) 102 } 103 return nil 104 }