github.com/rzurga/go-swagger@v0.28.1-0.20211109195225-5d1f453ffa3a/cmd/swagger/commands/diff/checks.go (about)

     1  package diff
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/go-openapi/spec"
     8  )
     9  
    10  // CompareEnums returns added, deleted enum values
    11  func CompareEnums(left, right []interface{}) []TypeDiff {
    12  	diffs := []TypeDiff{}
    13  
    14  	leftStrs := []string{}
    15  	rightStrs := []string{}
    16  	for _, eachLeft := range left {
    17  		leftStrs = append(leftStrs, fmt.Sprintf("%v", eachLeft))
    18  	}
    19  	for _, eachRight := range right {
    20  		rightStrs = append(rightStrs, fmt.Sprintf("%v", eachRight))
    21  	}
    22  	added, deleted, _ := fromStringArray(leftStrs).DiffsTo(rightStrs)
    23  	if len(added) > 0 {
    24  		typeChange := strings.Join(added, ",")
    25  		diffs = append(diffs, TypeDiff{Change: AddedEnumValue, Description: typeChange})
    26  	}
    27  	if len(deleted) > 0 {
    28  		typeChange := strings.Join(deleted, ",")
    29  		diffs = append(diffs, TypeDiff{Change: DeletedEnumValue, Description: typeChange})
    30  	}
    31  
    32  	return diffs
    33  }
    34  
    35  // CompareProperties recursive property comparison
    36  func CompareProperties(location DifferenceLocation, schema1 *spec.Schema, schema2 *spec.Schema, getRefFn1 SchemaFromRefFn, getRefFn2 SchemaFromRefFn, cmp CompareSchemaFn) []SpecDifference {
    37  	propDiffs := []SpecDifference{}
    38  
    39  	if schema1.Properties == nil && schema2.Properties == nil {
    40  		return propDiffs
    41  	}
    42  
    43  	schema1Props := propertiesFor(schema1, getRefFn1)
    44  	schema2Props := propertiesFor(schema2, getRefFn2)
    45  	// find deleted and changed properties
    46  
    47  	for eachProp1Name, eachProp1 := range schema1Props {
    48  		eachProp1 := eachProp1
    49  		childLoc := addChildDiffNode(location, eachProp1Name, eachProp1.Schema)
    50  
    51  		if eachProp2, ok := schema2Props[eachProp1Name]; ok {
    52  			diffs := CheckToFromRequired(eachProp1.Required, eachProp2.Required)
    53  			if len(diffs) > 0 {
    54  				for _, diff := range diffs {
    55  					propDiffs = append(propDiffs, SpecDifference{DifferenceLocation: childLoc, Code: diff.Change})
    56  				}
    57  			}
    58  			cmp(childLoc, eachProp1.Schema, eachProp2.Schema)
    59  		} else {
    60  			propDiffs = append(propDiffs, SpecDifference{DifferenceLocation: childLoc, Code: DeletedProperty})
    61  		}
    62  	}
    63  
    64  	// find added properties
    65  	for eachProp2Name, eachProp2 := range schema2.Properties {
    66  		eachProp2 := eachProp2
    67  		if _, ok := schema1.Properties[eachProp2Name]; !ok {
    68  			childLoc := addChildDiffNode(location, eachProp2Name, &eachProp2)
    69  			propDiffs = append(propDiffs, SpecDifference{DifferenceLocation: childLoc, Code: AddedProperty})
    70  		}
    71  	}
    72  	return propDiffs
    73  
    74  }
    75  
    76  // CompareFloatValues compares a float data item
    77  func CompareFloatValues(fieldName string, val1 *float64, val2 *float64, ifGreaterCode SpecChangeCode, ifLessCode SpecChangeCode) []TypeDiff {
    78  	diffs := []TypeDiff{}
    79  	if val1 != nil && val2 != nil {
    80  		if *val2 > *val1 {
    81  			diffs = append(diffs, TypeDiff{Change: ifGreaterCode, Description: fmt.Sprintf("%s %f->%f", fieldName, *val1, *val2)})
    82  		} else if *val2 < *val1 {
    83  			diffs = append(diffs, TypeDiff{Change: ifLessCode, Description: fmt.Sprintf("%s %f->%f", fieldName, *val1, *val2)})
    84  		}
    85  	} else {
    86  		if val1 != val2 {
    87  			if val1 != nil {
    88  				diffs = append(diffs, TypeDiff{Change: DeletedConstraint, Description: fmt.Sprintf("%s(%f)", fieldName, *val1)})
    89  			} else {
    90  				diffs = append(diffs, TypeDiff{Change: AddedConstraint, Description: fmt.Sprintf("%s(%f)", fieldName, *val2)})
    91  			}
    92  		}
    93  	}
    94  	return diffs
    95  }
    96  
    97  // CompareIntValues compares to int data items
    98  func CompareIntValues(fieldName string, val1 *int64, val2 *int64, ifGreaterCode SpecChangeCode, ifLessCode SpecChangeCode) []TypeDiff {
    99  	diffs := []TypeDiff{}
   100  	if val1 != nil && val2 != nil {
   101  		if *val2 > *val1 {
   102  			diffs = append(diffs, TypeDiff{Change: ifGreaterCode, Description: fmt.Sprintf("%s %d->%d", fieldName, *val1, *val2)})
   103  		} else if *val2 < *val1 {
   104  			diffs = append(diffs, TypeDiff{Change: ifLessCode, Description: fmt.Sprintf("%s %d->%d", fieldName, *val1, *val2)})
   105  		}
   106  	} else {
   107  		if val1 != val2 {
   108  			if val1 != nil {
   109  				diffs = append(diffs, TypeDiff{Change: DeletedConstraint, Description: fmt.Sprintf("%s(%d)", fieldName, *val1)})
   110  			} else {
   111  				diffs = append(diffs, TypeDiff{Change: AddedConstraint, Description: fmt.Sprintf("%s(%d)", fieldName, *val2)})
   112  			}
   113  		}
   114  	}
   115  	return diffs
   116  }
   117  
   118  // CheckToFromPrimitiveType check for diff to or from a primitive
   119  func CheckToFromPrimitiveType(diffs []TypeDiff, type1, type2 interface{}) []TypeDiff {
   120  
   121  	type1IsPrimitive := isPrimitive(type1)
   122  	type2IsPrimitive := isPrimitive(type2)
   123  
   124  	// Primitive to Obj or Obj to Primitive
   125  	if type1IsPrimitive != type2IsPrimitive {
   126  		typeStr1, isarray1 := getSchemaType(type1)
   127  		typeStr2, isarray2 := getSchemaType(type2)
   128  		return addTypeDiff(diffs, TypeDiff{Change: ChangedType, FromType: formatTypeString(typeStr1, isarray1), ToType: formatTypeString(typeStr2, isarray2)})
   129  	}
   130  
   131  	return diffs
   132  }
   133  
   134  // CheckRefChange has the property ref changed
   135  func CheckRefChange(diffs []TypeDiff, type1, type2 interface{}) (diffReturn []TypeDiff) {
   136  
   137  	diffReturn = diffs
   138  	if isRefType(type1) && isRefType(type2) {
   139  		// both refs but to different objects (TODO detect renamed object)
   140  		ref1 := definitionFromRef(getRef(type1))
   141  		ref2 := definitionFromRef(getRef(type2))
   142  		if ref1 != ref2 {
   143  			diffReturn = addTypeDiff(diffReturn, TypeDiff{Change: RefTargetChanged, FromType: getSchemaTypeStr(type1), ToType: getSchemaTypeStr(type2)})
   144  		}
   145  	} else if isRefType(type1) != isRefType(type2) {
   146  		diffReturn = addTypeDiff(diffReturn, TypeDiff{Change: ChangedType, FromType: getSchemaTypeStr(type1), ToType: getSchemaTypeStr(type2)})
   147  	}
   148  	return
   149  }
   150  
   151  // checkNumericTypeChanges checks for changes to or from a numeric type
   152  func checkNumericTypeChanges(diffs []TypeDiff, type1, type2 *spec.SchemaProps) []TypeDiff {
   153  	// Number
   154  	_, type1IsNumeric := numberWideness[type1.Type[0]]
   155  	_, type2IsNumeric := numberWideness[type2.Type[0]]
   156  
   157  	if type1IsNumeric && type2IsNumeric {
   158  		foundDiff := false
   159  		if type1.ExclusiveMaximum && !type2.ExclusiveMaximum {
   160  			diffs = addTypeDiff(diffs, TypeDiff{Change: WidenedType, Description: fmt.Sprintf("Exclusive Maximum Removed:%v->%v", type1.ExclusiveMaximum, type2.ExclusiveMaximum)})
   161  			foundDiff = true
   162  		}
   163  		if !type1.ExclusiveMaximum && type2.ExclusiveMaximum {
   164  			diffs = addTypeDiff(diffs, TypeDiff{Change: NarrowedType, Description: fmt.Sprintf("Exclusive Maximum Added:%v->%v", type1.ExclusiveMaximum, type2.ExclusiveMaximum)})
   165  			foundDiff = true
   166  		}
   167  		if type1.ExclusiveMinimum && !type2.ExclusiveMinimum {
   168  			diffs = addTypeDiff(diffs, TypeDiff{Change: WidenedType, Description: fmt.Sprintf("Exclusive Minimum Removed:%v->%v", type1.ExclusiveMaximum, type2.ExclusiveMaximum)})
   169  			foundDiff = true
   170  		}
   171  		if !type1.ExclusiveMinimum && type2.ExclusiveMinimum {
   172  			diffs = addTypeDiff(diffs, TypeDiff{Change: NarrowedType, Description: fmt.Sprintf("Exclusive Minimum Added:%v->%v", type1.ExclusiveMinimum, type2.ExclusiveMinimum)})
   173  			foundDiff = true
   174  		}
   175  		if !foundDiff {
   176  			maxDiffs := CompareFloatValues("Maximum", type1.Maximum, type2.Maximum, WidenedType, NarrowedType)
   177  			diffs = append(diffs, maxDiffs...)
   178  			minDiffs := CompareFloatValues("Minimum", type1.Minimum, type2.Minimum, NarrowedType, WidenedType)
   179  			diffs = append(diffs, minDiffs...)
   180  		}
   181  	}
   182  	return diffs
   183  }
   184  
   185  // CheckStringTypeChanges checks for changes to or from a string type
   186  func CheckStringTypeChanges(diffs []TypeDiff, type1, type2 *spec.SchemaProps) []TypeDiff {
   187  	// string changes
   188  	if type1.Type[0] == StringType &&
   189  		type2.Type[0] == StringType {
   190  		minLengthDiffs := CompareIntValues("MinLength", type1.MinLength, type2.MinLength, NarrowedType, WidenedType)
   191  		diffs = append(diffs, minLengthDiffs...)
   192  		maxLengthDiffs := CompareIntValues("MaxLength", type1.MinLength, type2.MinLength, WidenedType, NarrowedType)
   193  		diffs = append(diffs, maxLengthDiffs...)
   194  		if type1.Pattern != type2.Pattern {
   195  			diffs = addTypeDiff(diffs, TypeDiff{Change: ChangedType, Description: fmt.Sprintf("Pattern Changed:%s->%s", type1.Pattern, type2.Pattern)})
   196  		}
   197  		if type1.Type[0] == StringType {
   198  			if len(type1.Enum) > 0 {
   199  				enumDiffs := CompareEnums(type1.Enum, type2.Enum)
   200  				diffs = append(diffs, enumDiffs...)
   201  			}
   202  		}
   203  	}
   204  	return diffs
   205  }
   206  
   207  // CheckToFromRequired checks for changes to or from a required property
   208  func CheckToFromRequired(required1, required2 bool) (diffs []TypeDiff) {
   209  	if required1 != required2 {
   210  		code := ChangedOptionalToRequired
   211  		if required1 {
   212  			code = ChangedRequiredToOptional
   213  		}
   214  		diffs = addTypeDiff(diffs, TypeDiff{Change: code})
   215  	}
   216  	return diffs
   217  }
   218  
   219  const objType = "object"
   220  
   221  func getTypeHierarchyChange(type1, type2 string) TypeDiff {
   222  	fromType := type1
   223  	if fromType == "" {
   224  		fromType = objType
   225  	}
   226  	toType := type2
   227  	if toType == "" {
   228  		toType = objType
   229  	}
   230  	diffDescription := fmt.Sprintf("%s -> %s", fromType, toType)
   231  	if isStringType(type1) && !isStringType(type2) {
   232  		return TypeDiff{Change: NarrowedType, Description: diffDescription}
   233  	}
   234  	if !isStringType(type1) && isStringType(type2) {
   235  		return TypeDiff{Change: WidenedType, Description: diffDescription}
   236  	}
   237  	type1Wideness, type1IsNumeric := numberWideness[type1]
   238  	type2Wideness, type2IsNumeric := numberWideness[type2]
   239  	if type1IsNumeric && type2IsNumeric {
   240  		if type1Wideness == type2Wideness {
   241  			return TypeDiff{Change: ChangedToCompatibleType, Description: diffDescription}
   242  		}
   243  		if type1Wideness > type2Wideness {
   244  			return TypeDiff{Change: NarrowedType, Description: diffDescription}
   245  		}
   246  		if type1Wideness < type2Wideness {
   247  			return TypeDiff{Change: WidenedType, Description: diffDescription}
   248  		}
   249  	}
   250  	return TypeDiff{Change: ChangedType, Description: diffDescription}
   251  }
   252  
   253  func isRefType(item interface{}) bool {
   254  	switch s := item.(type) {
   255  	case spec.Refable:
   256  		return s.Ref.String() != ""
   257  	case *spec.Schema:
   258  		return s.Ref.String() != ""
   259  	case *spec.SchemaProps:
   260  		return s.Ref.String() != ""
   261  	case *spec.SimpleSchema:
   262  		return false
   263  	default:
   264  		return false
   265  	}
   266  }