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 }