github.com/snyk/vervet/v4@v4.27.2/merge.go (about) 1 package vervet 2 3 import ( 4 "sort" 5 6 "github.com/getkin/kin-openapi/openapi3" 7 ) 8 9 // Merge adds the paths and components from a source OpenAPI document root, 10 // to a destination document root. 11 // 12 // TODO: This is a naive implementation that should be improved to detect and 13 // resolve conflicts better. For example, distinct resources might have 14 // localized references with the same URIs but different content. 15 // Content-addressible resource versions may further facilitate governance; 16 // this also would facilitate detecting and relocating such conflicts. 17 // 18 // TODO(next-release): 19 // - This function is suitable for overlay merging scenarios only. 20 // - Component merging should be removed. Use Collator for safe component 21 // merging. 22 func Merge(dst, src *openapi3.T, replace bool) { 23 mergeComponents(dst, src, replace) 24 mergeExtensions(dst, src, replace) 25 mergeInfo(dst, src, replace) 26 mergeOpenAPIVersion(dst, src, replace) 27 mergePaths(dst, src, replace) 28 mergeSecurityRequirements(dst, src, replace) 29 mergeServers(dst, src, replace) 30 mergeTags(dst, src, replace) 31 } 32 33 func mergeOpenAPIVersion(dst, src *openapi3.T, replace bool) { 34 if dst.OpenAPI == "" || (src.OpenAPI != "" && replace) { 35 dst.OpenAPI = src.OpenAPI 36 } 37 } 38 39 func mergeTags(dst, src *openapi3.T, replace bool) { 40 m := map[string]*openapi3.Tag{} 41 for _, t := range dst.Tags { 42 m[t.Name] = t 43 } 44 for _, t := range src.Tags { 45 if _, ok := m[t.Name]; !ok || replace { 46 m[t.Name] = t 47 } 48 } 49 dst.Tags = openapi3.Tags{} 50 var tagNames []string 51 for tagName := range m { 52 tagNames = append(tagNames, tagName) 53 } 54 sort.Strings(tagNames) 55 for _, tagName := range tagNames { 56 dst.Tags = append(dst.Tags, m[tagName]) 57 } 58 } 59 60 func initDestinationComponents(dst, src *openapi3.T) { 61 if src.Components.Schemas != nil && dst.Components.Schemas == nil { 62 dst.Components.Schemas = make(map[string]*openapi3.SchemaRef) 63 } 64 if src.Components.Parameters != nil && dst.Components.Parameters == nil { 65 dst.Components.Parameters = make(map[string]*openapi3.ParameterRef) 66 } 67 if src.Components.Headers != nil && dst.Components.Headers == nil { 68 dst.Components.Headers = make(map[string]*openapi3.HeaderRef) 69 } 70 if src.Components.RequestBodies != nil && dst.Components.RequestBodies == nil { 71 dst.Components.RequestBodies = make(map[string]*openapi3.RequestBodyRef) 72 } 73 if src.Components.Responses != nil && dst.Components.Responses == nil { 74 dst.Components.Responses = make(map[string]*openapi3.ResponseRef) 75 } 76 if src.Components.SecuritySchemes != nil && dst.Components.SecuritySchemes == nil { 77 dst.Components.SecuritySchemes = make(map[string]*openapi3.SecuritySchemeRef) 78 } 79 if src.Components.Examples != nil && dst.Components.Examples == nil { 80 dst.Components.Examples = make(map[string]*openapi3.ExampleRef) 81 } 82 if src.Components.Links != nil && dst.Components.Links == nil { 83 dst.Components.Links = make(map[string]*openapi3.LinkRef) 84 } 85 if src.Components.Callbacks != nil && dst.Components.Callbacks == nil { 86 dst.Components.Callbacks = make(map[string]*openapi3.CallbackRef) 87 } 88 } 89 90 func mergeComponents(dst, src *openapi3.T, replace bool) { 91 initDestinationComponents(dst, src) 92 for k, v := range src.Components.Schemas { 93 if _, ok := dst.Components.Schemas[k]; !ok || replace { 94 dst.Components.Schemas[k] = v 95 } 96 } 97 for k, v := range src.Components.Parameters { 98 if _, ok := dst.Components.Parameters[k]; !ok || replace { 99 dst.Components.Parameters[k] = v 100 } 101 } 102 for k, v := range src.Components.Headers { 103 if _, ok := dst.Components.Headers[k]; !ok || replace { 104 dst.Components.Headers[k] = v 105 } 106 } 107 for k, v := range src.Components.RequestBodies { 108 if _, ok := dst.Components.RequestBodies[k]; !ok || replace { 109 dst.Components.RequestBodies[k] = v 110 } 111 } 112 for k, v := range src.Components.Responses { 113 if _, ok := dst.Components.Responses[k]; !ok || replace { 114 dst.Components.Responses[k] = v 115 } 116 } 117 for k, v := range src.Components.SecuritySchemes { 118 if _, ok := dst.Components.SecuritySchemes[k]; !ok || replace { 119 dst.Components.SecuritySchemes[k] = v 120 } 121 } 122 for k, v := range src.Components.Examples { 123 if _, ok := dst.Components.Examples[k]; !ok || replace { 124 dst.Components.Examples[k] = v 125 } 126 } 127 for k, v := range src.Components.Links { 128 if _, ok := dst.Components.Links[k]; !ok || replace { 129 dst.Components.Links[k] = v 130 } 131 } 132 for k, v := range src.Components.Callbacks { 133 if _, ok := dst.Components.Callbacks[k]; !ok || replace { 134 dst.Components.Callbacks[k] = v 135 } 136 } 137 } 138 139 func mergeExtensions(dst, src *openapi3.T, replace bool) { 140 if src.Extensions != nil && dst.Extensions == nil { 141 dst.Extensions = make(map[string]interface{}, len(src.Extensions)) 142 } 143 144 for k, v := range src.Extensions { 145 // It's possible for specs to be merged from multiple stabilities and 146 // we don't want these different stability inputs to override 147 // the declared stability of the output we're building. 148 if k == ExtSnykApiStability { 149 continue 150 } 151 152 if _, ok := dst.Extensions[k]; !ok || replace { 153 dst.Extensions[k] = v 154 } 155 } 156 } 157 158 func mergeInfo(dst, src *openapi3.T, replace bool) { 159 if src.Info != nil && (dst.Info == nil || replace) { 160 dst.Info = src.Info 161 } 162 } 163 164 func mergePaths(dst, src *openapi3.T, replace bool) { 165 if src.Paths != nil && dst.Paths == nil { 166 dst.Paths = make(openapi3.Paths, len(src.Paths)) 167 } 168 for k, v := range src.Paths { 169 if _, ok := dst.Paths[k]; !ok || replace { 170 dst.Paths[k] = v 171 } 172 } 173 } 174 175 func mergeSecurityRequirements(dst, src *openapi3.T, replace bool) { 176 if len(src.Security) > 0 && (len(dst.Security) == 0 || replace) { 177 dst.Security = src.Security 178 } 179 } 180 181 func mergeServers(dst, src *openapi3.T, replace bool) { 182 if len(src.Servers) > 0 && (len(dst.Security) == 0 || replace) { 183 dst.Servers = src.Servers 184 } 185 }