github.com/snyk/vervet/v3@v3.7.0/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  func Merge(dst, src *openapi3.T, replace bool) {
    18  	mergeComponents(dst, src, replace)
    19  	mergeInfo(dst, src, replace)
    20  	mergePaths(dst, src, replace)
    21  	mergeSecurityRequirements(dst, src, replace)
    22  	mergeServers(dst, src, replace)
    23  	mergeTags(dst, src, replace)
    24  }
    25  
    26  func mergeTags(dst, src *openapi3.T, replace bool) {
    27  	m := map[string]*openapi3.Tag{}
    28  	for _, t := range dst.Tags {
    29  		m[t.Name] = t
    30  	}
    31  	for _, t := range src.Tags {
    32  		if _, ok := m[t.Name]; !ok || replace {
    33  			m[t.Name] = t
    34  		}
    35  	}
    36  	dst.Tags = openapi3.Tags{}
    37  	var tagNames []string
    38  	for tagName := range m {
    39  		tagNames = append(tagNames, tagName)
    40  	}
    41  	sort.Strings(tagNames)
    42  	for _, tagName := range tagNames {
    43  		dst.Tags = append(dst.Tags, m[tagName])
    44  	}
    45  }
    46  
    47  func initDestinationComponents(dst, src *openapi3.T) {
    48  	if src.Components.Schemas != nil && dst.Components.Schemas == nil {
    49  		dst.Components.Schemas = make(map[string]*openapi3.SchemaRef)
    50  	}
    51  	if src.Components.Parameters != nil && dst.Components.Parameters == nil {
    52  		dst.Components.Parameters = make(map[string]*openapi3.ParameterRef)
    53  	}
    54  	if src.Components.Headers != nil && dst.Components.Headers == nil {
    55  		dst.Components.Headers = make(map[string]*openapi3.HeaderRef)
    56  	}
    57  	if src.Components.RequestBodies != nil && dst.Components.RequestBodies == nil {
    58  		dst.Components.RequestBodies = make(map[string]*openapi3.RequestBodyRef)
    59  	}
    60  	if src.Components.Responses != nil && dst.Components.Responses == nil {
    61  		dst.Components.Responses = make(map[string]*openapi3.ResponseRef)
    62  	}
    63  	if src.Components.SecuritySchemes != nil && dst.Components.SecuritySchemes == nil {
    64  		dst.Components.SecuritySchemes = make(map[string]*openapi3.SecuritySchemeRef)
    65  	}
    66  	if src.Components.Examples != nil && dst.Components.Examples == nil {
    67  		dst.Components.Examples = make(map[string]*openapi3.ExampleRef)
    68  	}
    69  	if src.Components.Links != nil && dst.Components.Links == nil {
    70  		dst.Components.Links = make(map[string]*openapi3.LinkRef)
    71  	}
    72  	if src.Components.Callbacks != nil && dst.Components.Callbacks == nil {
    73  		dst.Components.Callbacks = make(map[string]*openapi3.CallbackRef)
    74  	}
    75  }
    76  
    77  func mergeComponents(dst, src *openapi3.T, replace bool) {
    78  	initDestinationComponents(dst, src)
    79  	for k, v := range src.Components.Schemas {
    80  		if _, ok := dst.Components.Schemas[k]; !ok || replace {
    81  			dst.Components.Schemas[k] = v
    82  		}
    83  	}
    84  	for k, v := range src.Components.Parameters {
    85  		if _, ok := dst.Components.Parameters[k]; !ok || replace {
    86  			dst.Components.Parameters[k] = v
    87  		}
    88  	}
    89  	for k, v := range src.Components.Headers {
    90  		if _, ok := dst.Components.Headers[k]; !ok || replace {
    91  			dst.Components.Headers[k] = v
    92  		}
    93  	}
    94  	for k, v := range src.Components.RequestBodies {
    95  		if _, ok := dst.Components.RequestBodies[k]; !ok || replace {
    96  			dst.Components.RequestBodies[k] = v
    97  		}
    98  	}
    99  	for k, v := range src.Components.Responses {
   100  		if _, ok := dst.Components.Responses[k]; !ok || replace {
   101  			dst.Components.Responses[k] = v
   102  		}
   103  	}
   104  	for k, v := range src.Components.SecuritySchemes {
   105  		if _, ok := dst.Components.SecuritySchemes[k]; !ok || replace {
   106  			dst.Components.SecuritySchemes[k] = v
   107  		}
   108  	}
   109  	for k, v := range src.Components.Examples {
   110  		if _, ok := dst.Components.Examples[k]; !ok || replace {
   111  			dst.Components.Examples[k] = v
   112  		}
   113  	}
   114  	for k, v := range src.Components.Links {
   115  		if _, ok := dst.Components.Links[k]; !ok || replace {
   116  			dst.Components.Links[k] = v
   117  		}
   118  	}
   119  	for k, v := range src.Components.Callbacks {
   120  		if _, ok := dst.Components.Callbacks[k]; !ok || replace {
   121  			dst.Components.Callbacks[k] = v
   122  		}
   123  	}
   124  }
   125  
   126  func mergeInfo(dst, src *openapi3.T, replace bool) {
   127  	if src.Info != nil && (dst.Info == nil || replace) {
   128  		dst.Info = src.Info
   129  	}
   130  }
   131  
   132  func mergePaths(dst, src *openapi3.T, replace bool) {
   133  	if src.Paths != nil && dst.Paths == nil {
   134  		dst.Paths = make(openapi3.Paths)
   135  	}
   136  	for k, v := range src.Paths {
   137  		if _, ok := dst.Paths[k]; !ok || replace {
   138  			dst.Paths[k] = v
   139  		}
   140  	}
   141  }
   142  
   143  func mergeSecurityRequirements(dst, src *openapi3.T, replace bool) {
   144  	if len(src.Security) > 0 && (len(dst.Security) == 0 || replace) {
   145  		dst.Security = src.Security
   146  	}
   147  }
   148  
   149  func mergeServers(dst, src *openapi3.T, replace bool) {
   150  	if len(src.Servers) > 0 && (len(dst.Security) == 0 || replace) {
   151  		dst.Servers = src.Servers
   152  	}
   153  }