Files
libopenapi/what-changed/model/header.go
Dave Shanley 61f99b8fd6 Replacing extensions hash code **breaking change**
This is a large update, I realized that extensions are not being hashed correctly, and because I have the same code everywhere, it means running back through the stack and cleaning up the invalid code that will break if multiple extensions are used in different positions in the raw spec.

At the same time, I realized that the v2 model has the same primitive/enum issues that are part cleaned up in v3. This is a breaking changhe because enums are now []any and not []string, as well as primitives for bool, int etc are all pointers now instead of the copied values.

This will break any consumers.
2022-11-18 11:00:34 -05:00

246 lines
8.4 KiB
Go

// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/low/v3"
"reflect"
)
type HeaderChanges struct {
PropertyChanges
SchemaChanges *SchemaChanges
ExamplesChanges map[string]*ExampleChanges
ContentChanges map[string]*MediaTypeChanges
ExtensionChanges *ExtensionChanges
// V2 changes
ItemsChanges *ItemsChanges
}
func (h *HeaderChanges) TotalChanges() int {
c := h.PropertyChanges.TotalChanges()
for k := range h.ExamplesChanges {
c += h.ExamplesChanges[k].TotalChanges()
}
for k := range h.ContentChanges {
c += h.ContentChanges[k].TotalChanges()
}
if h.ExtensionChanges != nil {
c += h.ExtensionChanges.TotalChanges()
}
if h.SchemaChanges != nil {
c += h.SchemaChanges.TotalChanges()
}
if h.ItemsChanges != nil {
c += h.ItemsChanges.TotalChanges()
}
return c
}
func (h *HeaderChanges) TotalBreakingChanges() int {
c := h.PropertyChanges.TotalBreakingChanges()
for k := range h.ContentChanges {
c += h.ContentChanges[k].TotalBreakingChanges()
}
if h.ItemsChanges != nil {
c += h.ItemsChanges.TotalBreakingChanges()
}
if h.SchemaChanges != nil {
c += h.SchemaChanges.TotalBreakingChanges()
}
return c
}
func addOpenAPIHeaderProperties(left, right low.OpenAPIHeader, changes *[]*Change) []*PropertyCheck {
var props []*PropertyCheck
// style
addPropertyCheck(&props, left.GetStyle().ValueNode, right.GetStyle().ValueNode,
left.GetStyle(), right.GetStyle(), changes, v3.StyleLabel, false)
// allow reserved
addPropertyCheck(&props, left.GetAllowReserved().ValueNode, right.GetAllowReserved().ValueNode,
left.GetAllowReserved(), right.GetAllowReserved(), changes, v3.AllowReservedLabel, false)
// allow empty value
addPropertyCheck(&props, left.GetAllowEmptyValue().ValueNode, right.GetAllowEmptyValue().ValueNode,
left.GetAllowEmptyValue(), right.GetAllowEmptyValue(), changes, v3.AllowEmptyValueLabel, true)
// explode
addPropertyCheck(&props, left.GetExplode().ValueNode, right.GetExplode().ValueNode,
left.GetExplode(), right.GetExplode(), changes, v3.ExplodeLabel, false)
// example
addPropertyCheck(&props, left.GetExample().ValueNode, right.GetExample().ValueNode,
left.GetExample(), right.GetExample(), changes, v3.ExampleLabel, false)
// deprecated
addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode,
left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, false)
// required
addPropertyCheck(&props, left.GetRequired().ValueNode, right.GetRequired().ValueNode,
left.GetRequired(), right.GetRequired(), changes, v3.RequiredLabel, true)
return props
}
func addSwaggerHeaderProperties(left, right low.SwaggerHeader, changes *[]*Change) []*PropertyCheck {
var props []*PropertyCheck
// type
addPropertyCheck(&props, left.GetType().ValueNode, right.GetType().ValueNode,
left.GetType(), right.GetType(), changes, v3.TypeLabel, true)
// format
addPropertyCheck(&props, left.GetFormat().ValueNode, right.GetFormat().ValueNode,
left.GetFormat(), right.GetFormat(), changes, v3.FormatLabel, true)
// collection format
addPropertyCheck(&props, left.GetCollectionFormat().ValueNode, right.GetCollectionFormat().ValueNode,
left.GetCollectionFormat(), right.GetCollectionFormat(), changes, v3.CollectionFormatLabel, true)
// maximum
addPropertyCheck(&props, left.GetMaximum().ValueNode, right.GetMaximum().ValueNode,
left.GetMaximum(), right.GetMaximum(), changes, v3.MaximumLabel, true)
// minimum
addPropertyCheck(&props, left.GetMinimum().ValueNode, right.GetMinimum().ValueNode,
left.GetMinimum(), right.GetMinimum(), changes, v3.MinimumLabel, true)
// exclusive maximum
addPropertyCheck(&props, left.GetExclusiveMaximum().ValueNode, right.GetExclusiveMaximum().ValueNode,
left.GetExclusiveMaximum(), right.GetExclusiveMaximum(), changes, v3.ExclusiveMaximumLabel, true)
// exclusive minimum
addPropertyCheck(&props, left.GetExclusiveMinimum().ValueNode, right.GetExclusiveMinimum().ValueNode,
left.GetExclusiveMinimum(), right.GetExclusiveMinimum(), changes, v3.ExclusiveMinimumLabel, true)
// max length
addPropertyCheck(&props, left.GetMaxLength().ValueNode, right.GetMaxLength().ValueNode,
left.GetMaxLength(), right.GetMaxLength(), changes, v3.MaxLengthLabel, true)
// min length
addPropertyCheck(&props, left.GetMinLength().ValueNode, right.GetMinLength().ValueNode,
left.GetMinLength(), right.GetMinLength(), changes, v3.MinLengthLabel, true)
// pattern
addPropertyCheck(&props, left.GetPattern().ValueNode, right.GetPattern().ValueNode,
left.GetPattern(), right.GetPattern(), changes, v3.PatternLabel, true)
// max items
addPropertyCheck(&props, left.GetMaxItems().ValueNode, right.GetMaxItems().ValueNode,
left.GetMaxItems(), right.GetMaxItems(), changes, v3.MaxItemsLabel, true)
// min items
addPropertyCheck(&props, left.GetMinItems().ValueNode, right.GetMinItems().ValueNode,
left.GetMinItems(), right.GetMinItems(), changes, v3.MinItemsLabel, true)
// unique items
addPropertyCheck(&props, left.GetUniqueItems().ValueNode, right.GetUniqueItems().ValueNode,
left.GetUniqueItems(), right.GetUniqueItems(), changes, v3.UniqueItemsLabel, true)
// multiple of
addPropertyCheck(&props, left.GetMultipleOf().ValueNode, right.GetMultipleOf().ValueNode,
left.GetMultipleOf(), right.GetMultipleOf(), changes, v3.MultipleOfLabel, true)
return props
}
func addCommonHeaderProperties(left, right low.HasDescription, changes *[]*Change) []*PropertyCheck {
var props []*PropertyCheck
// description
addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode,
left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, false)
return props
}
func CompareHeadersV2(l, r *v2.Header) *HeaderChanges {
return CompareHeaders(l, r)
}
func CompareHeadersV3(l, r *v3.Header) *HeaderChanges {
return CompareHeaders(l, r)
}
func CompareHeaders(l, r any) *HeaderChanges {
var changes []*Change
var props []*PropertyCheck
hc := new(HeaderChanges)
// handle swagger.
if reflect.TypeOf(&v2.Header{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Header{}) == reflect.TypeOf(r) {
lHeader := l.(*v2.Header)
rHeader := r.(*v2.Header)
// perform hash check to avoid further processing
if low.AreEqual(lHeader, rHeader) {
return nil
}
props = append(props, addCommonHeaderProperties(lHeader, rHeader, &changes)...)
props = append(props, addSwaggerHeaderProperties(lHeader, rHeader, &changes)...)
// enum
if len(lHeader.Enum.Value) > 0 || len(rHeader.Enum.Value) > 0 {
ExtractRawValueSliceChanges(lHeader.Enum.Value, rHeader.Enum.Value, &changes, v3.EnumLabel, true)
}
// items
if !lHeader.Items.IsEmpty() && !rHeader.Items.IsEmpty() {
if !low.AreEqual(lHeader.Items.Value, rHeader.Items.Value) {
hc.ItemsChanges = CompareItems(lHeader.Items.Value, rHeader.Items.Value)
}
}
if lHeader.Items.IsEmpty() && !rHeader.Items.IsEmpty() {
CreateChange(&changes, ObjectAdded, v3.ItemsLabel, nil,
rHeader.Items.ValueNode, true, nil, rHeader.Items.Value)
}
if !lHeader.Items.IsEmpty() && rHeader.Items.IsEmpty() {
CreateChange(&changes, ObjectRemoved, v3.SchemaLabel, lHeader.Items.ValueNode,
nil, true, lHeader.Items.Value, nil)
}
hc.ExtensionChanges = CompareExtensions(lHeader.Extensions, rHeader.Extensions)
}
// handle OpenAPI
if reflect.TypeOf(&v3.Header{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Header{}) == reflect.TypeOf(r) {
lHeader := l.(*v3.Header)
rHeader := r.(*v3.Header)
// perform hash check to avoid further processing
if low.AreEqual(lHeader, rHeader) {
return nil
}
props = append(props, addCommonHeaderProperties(lHeader, rHeader, &changes)...)
props = append(props, addOpenAPIHeaderProperties(lHeader, rHeader, &changes)...)
// header
if !lHeader.Schema.IsEmpty() || !rHeader.Schema.IsEmpty() {
hc.SchemaChanges = CompareSchemas(lHeader.Schema.Value, rHeader.Schema.Value)
}
// examples
hc.ExamplesChanges = CheckMapForChanges(lHeader.Examples.Value, rHeader.Examples.Value,
&changes, v3.ExamplesLabel, CompareExamples)
// content
hc.ContentChanges = CheckMapForChanges(lHeader.Content.Value, rHeader.Content.Value,
&changes, v3.ContentLabel, CompareMediaTypes)
hc.ExtensionChanges = CompareExtensions(lHeader.Extensions, rHeader.Extensions)
}
CheckProperties(props)
hc.Changes = changes
return hc
}