mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-10 20:47:44 +00:00
Moved comparison function for map checking to be async, removed the untyped variation as it's no longer required. Getting prepared for tests
336 lines
11 KiB
Go
336 lines
11 KiB
Go
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package model
|
|
|
|
import (
|
|
"github.com/pb33f/libopenapi/datamodel/low"
|
|
"github.com/pb33f/libopenapi/datamodel/low/base"
|
|
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
|
|
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
|
"gopkg.in/yaml.v3"
|
|
"reflect"
|
|
)
|
|
|
|
type ParameterChanges struct {
|
|
PropertyChanges
|
|
SchemaChanges *SchemaChanges
|
|
ExtensionChanges *ExtensionChanges
|
|
|
|
// v2 change types
|
|
ItemsChanges *ItemsChanges
|
|
|
|
// v3 change types
|
|
ExamplesChanges map[string]*ExampleChanges
|
|
ContentChanges map[string]*MediaTypeChanges
|
|
}
|
|
|
|
// TotalChanges returns a count of everything that changed
|
|
func (p *ParameterChanges) TotalChanges() int {
|
|
c := p.PropertyChanges.TotalChanges()
|
|
if p.SchemaChanges != nil {
|
|
c += p.SchemaChanges.TotalChanges()
|
|
}
|
|
for i := range p.ExamplesChanges {
|
|
c += p.ExamplesChanges[i].TotalChanges()
|
|
}
|
|
if p.ItemsChanges != nil {
|
|
c += p.ItemsChanges.TotalChanges()
|
|
}
|
|
if p.ExtensionChanges != nil {
|
|
c += p.ExtensionChanges.TotalChanges()
|
|
}
|
|
for i := range p.ContentChanges {
|
|
c += p.ContentChanges[i].TotalChanges()
|
|
}
|
|
return c
|
|
}
|
|
|
|
// TotalBreakingChanges always returns 0 for ExternalDoc objects, they are non-binding.
|
|
func (p *ParameterChanges) TotalBreakingChanges() int {
|
|
c := p.PropertyChanges.TotalBreakingChanges()
|
|
if p.SchemaChanges != nil {
|
|
c += p.SchemaChanges.TotalBreakingChanges()
|
|
}
|
|
if p.ItemsChanges != nil {
|
|
c += p.ItemsChanges.TotalBreakingChanges()
|
|
}
|
|
for i := range p.ContentChanges {
|
|
c += p.ContentChanges[i].TotalBreakingChanges()
|
|
}
|
|
return c
|
|
}
|
|
|
|
func addPropertyCheck(props *[]*PropertyCheck,
|
|
lvn, rvn *yaml.Node, lv, rv any, changes *[]*Change, label string, breaking bool) {
|
|
*props = append(*props, &PropertyCheck{
|
|
LeftNode: lvn,
|
|
RightNode: rvn,
|
|
Label: label,
|
|
Changes: changes,
|
|
Breaking: breaking,
|
|
Original: lv,
|
|
New: rv,
|
|
})
|
|
}
|
|
|
|
func addOpenAPIParameterProperties(left, right low.OpenAPIParameter, 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, true)
|
|
|
|
// explode
|
|
addPropertyCheck(&props, left.GetExplode().ValueNode, right.GetExplode().ValueNode,
|
|
left.GetExplode(), right.GetExplode(), changes, v3.ExplodeLabel, false)
|
|
|
|
// deprecated
|
|
addPropertyCheck(&props, left.GetDeprecated().ValueNode, right.GetDeprecated().ValueNode,
|
|
left.GetDeprecated(), right.GetDeprecated(), changes, v3.DeprecatedLabel, false)
|
|
|
|
// example
|
|
addPropertyCheck(&props, left.GetExample().ValueNode, right.GetExample().ValueNode,
|
|
left.GetExample(), right.GetExample(), changes, v3.ExampleLabel, false)
|
|
|
|
return props
|
|
}
|
|
|
|
func addSwaggerParameterProperties(left, right low.SwaggerParameter, 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)
|
|
|
|
// default
|
|
addPropertyCheck(&props, left.GetDefault().ValueNode, right.GetDefault().ValueNode,
|
|
left.GetDefault(), right.GetDefault(), changes, v3.DefaultLabel, true)
|
|
|
|
// multiple of
|
|
addPropertyCheck(&props, left.GetMultipleOf().ValueNode, right.GetMultipleOf().ValueNode,
|
|
left.GetMultipleOf(), right.GetMultipleOf(), changes, v3.MultipleOfLabel, true)
|
|
|
|
return props
|
|
}
|
|
|
|
func addCommonParameterProperties(left, right low.SharedParameters, changes *[]*Change) []*PropertyCheck {
|
|
var props []*PropertyCheck
|
|
|
|
addPropertyCheck(&props, left.GetName().ValueNode, right.GetName().ValueNode,
|
|
left.GetName(), right.GetName(), changes, v3.NameLabel, true)
|
|
|
|
// in
|
|
addPropertyCheck(&props, left.GetIn().ValueNode, right.GetIn().ValueNode,
|
|
left.GetIn(), right.GetIn(), changes, v3.InLabel, true)
|
|
|
|
// description
|
|
addPropertyCheck(&props, left.GetDescription().ValueNode, right.GetDescription().ValueNode,
|
|
left.GetDescription(), right.GetDescription(), changes, v3.DescriptionLabel, false)
|
|
|
|
// required
|
|
addPropertyCheck(&props, left.GetRequired().ValueNode, right.GetRequired().ValueNode,
|
|
left.GetRequired(), right.GetRequired(), changes, v3.RequiredLabel, true)
|
|
|
|
// allow empty value
|
|
addPropertyCheck(&props, left.GetAllowEmptyValue().ValueNode, right.GetAllowEmptyValue().ValueNode,
|
|
left.GetAllowEmptyValue(), right.GetAllowEmptyValue(), changes, v3.AllowEmptyValueLabel, true)
|
|
|
|
return props
|
|
}
|
|
|
|
func CompareParametersV3(l, r *v3.Parameter) *ParameterChanges {
|
|
return CompareParameters(l, r)
|
|
}
|
|
|
|
func CompareParametersV2(l, r *v2.Parameter) *ParameterChanges {
|
|
return CompareParameters(l, r)
|
|
}
|
|
|
|
func CompareParameters(l, r any) *ParameterChanges {
|
|
|
|
var changes []*Change
|
|
var props []*PropertyCheck
|
|
|
|
pc := new(ParameterChanges)
|
|
var lSchema *base.SchemaProxy
|
|
var rSchema *base.SchemaProxy
|
|
var lext, rext map[low.KeyReference[string]]low.ValueReference[any]
|
|
|
|
if reflect.TypeOf(&v2.Parameter{}) == reflect.TypeOf(l) && reflect.TypeOf(&v2.Parameter{}) == reflect.TypeOf(r) {
|
|
lParam := l.(*v2.Parameter)
|
|
rParam := r.(*v2.Parameter)
|
|
|
|
// perform hash check to avoid further processing
|
|
if low.AreEqual(lParam, rParam) {
|
|
return nil
|
|
}
|
|
|
|
props = append(props, addSwaggerParameterProperties(lParam, rParam, &changes)...)
|
|
props = append(props, addCommonParameterProperties(lParam, rParam, &changes)...)
|
|
|
|
// extract schema
|
|
if lParam != nil {
|
|
lSchema = lParam.Schema.Value
|
|
lext = lParam.Extensions
|
|
}
|
|
if rParam != nil {
|
|
rext = rParam.Extensions
|
|
rSchema = rParam.Schema.Value
|
|
}
|
|
|
|
// items
|
|
if !lParam.Items.IsEmpty() && !rParam.Items.IsEmpty() {
|
|
if lParam.Items.Value.Hash() != rParam.Items.Value.Hash() {
|
|
pc.ItemsChanges = CompareItems(lParam.Items.Value, rParam.Items.Value)
|
|
}
|
|
}
|
|
if lParam.Items.IsEmpty() && !rParam.Items.IsEmpty() {
|
|
CreateChange(&changes, ObjectAdded, v3.ItemsLabel,
|
|
nil, rParam.Items.ValueNode, true, nil,
|
|
rParam.Items.Value)
|
|
}
|
|
if !lParam.Items.IsEmpty() && rParam.Items.IsEmpty() {
|
|
CreateChange(&changes, ObjectRemoved, v3.ItemsLabel,
|
|
lParam.Items.ValueNode, nil, true, lParam.Items.Value,
|
|
nil)
|
|
}
|
|
|
|
// enum
|
|
if len(lParam.Enum.Value) > 0 || len(rParam.Enum.Value) > 0 {
|
|
ExtractRawValueSliceChanges(lParam.Enum.Value, rParam.Enum.Value, &changes, v3.EnumLabel, true)
|
|
}
|
|
}
|
|
|
|
// OpenAPI
|
|
if reflect.TypeOf(&v3.Parameter{}) == reflect.TypeOf(l) && reflect.TypeOf(&v3.Parameter{}) == reflect.TypeOf(r) {
|
|
|
|
lParam := l.(*v3.Parameter)
|
|
rParam := r.(*v3.Parameter)
|
|
|
|
// perform hash check to avoid further processing
|
|
if low.AreEqual(lParam, rParam) {
|
|
return nil
|
|
}
|
|
|
|
props = append(props, addOpenAPIParameterProperties(lParam, rParam, &changes)...)
|
|
props = append(props, addCommonParameterProperties(lParam, rParam, &changes)...)
|
|
if lParam != nil {
|
|
lext = lParam.Extensions
|
|
lSchema = lParam.Schema.Value
|
|
}
|
|
if rParam != nil {
|
|
rext = rParam.Extensions
|
|
rSchema = rParam.Schema.Value
|
|
}
|
|
|
|
// example
|
|
checkParameterExample(lParam.Example, rParam.Example, changes)
|
|
|
|
// examples
|
|
pc.ExamplesChanges = CheckMapForChanges(lParam.Examples.Value, rParam.Examples.Value,
|
|
&changes, v3.ExamplesLabel, CompareExamples)
|
|
|
|
// content
|
|
pc.ContentChanges = CheckMapForChanges(lParam.Content.Value, rParam.Content.Value,
|
|
&changes, v3.ContentLabel, CompareMediaTypes)
|
|
}
|
|
CheckProperties(props)
|
|
|
|
if lSchema != nil && rSchema != nil {
|
|
pc.SchemaChanges = CompareSchemas(lSchema, rSchema)
|
|
}
|
|
if lSchema != nil && rSchema == nil {
|
|
CreateChange(&changes, ObjectRemoved, v3.SchemaLabel,
|
|
lSchema.GetValueNode(), nil, true, lSchema,
|
|
nil)
|
|
}
|
|
|
|
if lSchema == nil && rSchema != nil {
|
|
CreateChange(&changes, ObjectAdded, v3.SchemaLabel,
|
|
nil, rSchema.GetValueNode(), true, nil,
|
|
rSchema)
|
|
}
|
|
|
|
pc.Changes = changes
|
|
pc.ExtensionChanges = CompareExtensions(lext, rext)
|
|
if pc.TotalChanges() <= 0 {
|
|
return nil
|
|
}
|
|
return pc
|
|
}
|
|
|
|
func checkParameterExample(expLeft, expRight low.NodeReference[any], changes []*Change) {
|
|
if !expLeft.IsEmpty() && !expRight.IsEmpty() {
|
|
if low.GenerateHashString(expLeft.GetValue()) != low.GenerateHashString(expRight.GetValue()) {
|
|
CreateChange(&changes, Modified, v3.ExampleLabel,
|
|
expLeft.GetValueNode(), expRight.GetValueNode(), false,
|
|
expLeft.GetValue(), expRight.GetValue())
|
|
}
|
|
}
|
|
if expLeft.Value == nil && expRight.Value != nil {
|
|
CreateChange(&changes, PropertyAdded, v3.ExampleLabel,
|
|
nil, expRight.GetValueNode(), false,
|
|
nil, expRight.GetValue())
|
|
|
|
}
|
|
if expLeft.Value != nil && expRight.Value == nil {
|
|
CreateChange(&changes, PropertyRemoved, v3.ExampleLabel,
|
|
expLeft.GetValueNode(), nil, false,
|
|
expLeft.GetValue(), nil)
|
|
|
|
}
|
|
}
|