gitea/vendor/github.com/go-swagger/go-swagger/cmd/swagger/commands/diff/spec_analyser.go

655 lines
23 KiB
Go

package diff
import (
"fmt"
"strings"
"github.com/go-openapi/spec"
)
const StringType = "string"
// URLMethodResponse encapsulates these three elements to act as a map key
type URLMethodResponse struct {
Path string `json:"path"`
Method string `json:"method"`
Response string `json:"response"`
}
// MarshalText - for serializing as a map key
func (p URLMethod) MarshalText() (text []byte, err error) {
return []byte(fmt.Sprintf("%s %s", p.Path, p.Method)), nil
}
// URLMethods allows iteration of endpoints based on url and method
type URLMethods map[URLMethod]*PathItemOp
// SpecAnalyser contains all the differences for a Spec
type SpecAnalyser struct {
Diffs SpecDifferences
urlMethods1 URLMethods
urlMethods2 URLMethods
Definitions1 spec.Definitions
Definitions2 spec.Definitions
AlreadyComparedDefinitions map[string]bool
}
// NewSpecAnalyser returns an empty SpecDiffs
func NewSpecAnalyser() *SpecAnalyser {
return &SpecAnalyser{
Diffs: SpecDifferences{},
}
}
// Analyse the differences in two specs
func (sd *SpecAnalyser) Analyse(spec1, spec2 *spec.Swagger) error {
sd.Definitions1 = spec1.Definitions
sd.Definitions2 = spec2.Definitions
sd.urlMethods1 = getURLMethodsFor(spec1)
sd.urlMethods2 = getURLMethodsFor(spec2)
sd.analyseSpecMetadata(spec1, spec2)
sd.analyseEndpoints()
sd.analyseParams()
sd.analyseEndpointData()
sd.analyseResponseParams()
return nil
}
func (sd *SpecAnalyser) analyseSpecMetadata(spec1, spec2 *spec.Swagger) {
// breaking if it no longer consumes any formats
added, deleted, _ := FromStringArray(spec1.Consumes).DiffsTo(spec2.Consumes)
node := getNameOnlyDiffNode("Spec")
location := DifferenceLocation{Node: node}
consumesLoation := location.AddNode(getNameOnlyDiffNode("consumes"))
for _, eachAdded := range added {
sd.Diffs = sd.Diffs.addDiff(
SpecDifference{DifferenceLocation: consumesLoation, Code: AddedConsumesFormat, Compatibility: NonBreaking, DiffInfo: eachAdded})
}
for _, eachDeleted := range deleted {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: consumesLoation, Code: DeletedConsumesFormat, Compatibility: Breaking, DiffInfo: eachDeleted})
}
// // breaking if it no longer produces any formats
added, deleted, _ = FromStringArray(spec1.Produces).DiffsTo(spec2.Produces)
producesLocation := location.AddNode(getNameOnlyDiffNode("produces"))
for _, eachAdded := range added {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: producesLocation, Code: AddedProducesFormat, Compatibility: NonBreaking, DiffInfo: eachAdded})
}
for _, eachDeleted := range deleted {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: producesLocation, Code: DeletedProducesFormat, Compatibility: Breaking, DiffInfo: eachDeleted})
}
// // breaking if it no longer supports a scheme
added, deleted, _ = FromStringArray(spec1.Schemes).DiffsTo(spec2.Schemes)
schemesLocation := location.AddNode(getNameOnlyDiffNode("schemes"))
for _, eachAdded := range added {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: schemesLocation, Code: AddedSchemes, Compatibility: NonBreaking, DiffInfo: eachAdded})
}
for _, eachDeleted := range deleted {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: schemesLocation, Code: DeletedSchemes, Compatibility: Breaking, DiffInfo: eachDeleted})
}
// // host should be able to change without any issues?
sd.analyseMetaDataProperty(spec1.Info.Description, spec2.Info.Description, ChangedDescripton, NonBreaking)
// // host should be able to change without any issues?
sd.analyseMetaDataProperty(spec1.Host, spec2.Host, ChangedHostURL, Breaking)
// sd.Host = compareStrings(spec1.Host, spec2.Host)
// // Base Path change will break non generated clients
sd.analyseMetaDataProperty(spec1.BasePath, spec2.BasePath, ChangedBasePath, Breaking)
// TODO: what to do about security?
// Missing security scheme will break a client
// Security []map[string][]string `json:"security,omitempty"`
// Tags []Tag `json:"tags,omitempty"`
// ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"`
}
func (sd *SpecAnalyser) analyseEndpoints() {
sd.findDeletedEndpoints()
sd.findAddedEndpoints()
}
func (sd *SpecAnalyser) analyseEndpointData() {
for URLMethod, op2 := range sd.urlMethods2 {
if op1, ok := sd.urlMethods1[URLMethod]; ok {
addedTags, deletedTags, _ := FromStringArray(op1.Operation.Tags).DiffsTo(op2.Operation.Tags)
location := DifferenceLocation{URL: URLMethod.Path, Method: URLMethod.Method}
for _, eachAddedTag := range addedTags {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: location, Code: AddedTag, DiffInfo: eachAddedTag})
}
for _, eachDeletedTag := range deletedTags {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: location, Code: DeletedTag, DiffInfo: eachDeletedTag})
}
sd.compareDescripton(location, op1.Operation.Description, op2.Operation.Description)
}
}
}
func (sd *SpecAnalyser) analyseParams() {
locations := []string{"query", "path", "body", "header"}
for _, paramLocation := range locations {
rootNode := getNameOnlyDiffNode(strings.Title(paramLocation))
for URLMethod, op2 := range sd.urlMethods2 {
if op1, ok := sd.urlMethods1[URLMethod]; ok {
params1 := getParams(op1.ParentPathItem.Parameters, op1.Operation.Parameters, paramLocation)
params2 := getParams(op2.ParentPathItem.Parameters, op2.Operation.Parameters, paramLocation)
location := DifferenceLocation{URL: URLMethod.Path, Method: URLMethod.Method, Node: rootNode}
// detect deleted params
for paramName1, param1 := range params1 {
if _, ok := params2[paramName1]; !ok {
childLocation := location.AddNode(getSchemaDiffNode(paramName1, param1.Schema))
code := DeletedOptionalParam
if param1.Required {
code = DeletedRequiredParam
}
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: childLocation, Code: code})
}
}
// detect added changed params
for paramName2, param2 := range params2 {
//changed?
if param1, ok := params1[paramName2]; ok {
sd.compareParams(URLMethod, paramLocation, paramName2, param1, param2)
} else {
// Added
childLocation := location.AddNode(getSchemaDiffNode(paramName2, param2.Schema))
code := AddedOptionalParam
if param2.Required {
code = AddedRequiredParam
}
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: childLocation, Code: code})
}
}
}
}
}
}
func (sd *SpecAnalyser) analyseResponseParams() {
// Loop through url+methods in spec 2 - check deleted and changed
for URLMethod2, op2 := range sd.urlMethods2 {
if op1, ok := sd.urlMethods1[URLMethod2]; ok {
// compare responses for url and method
op1Responses := op1.Operation.Responses.StatusCodeResponses
op2Responses := op2.Operation.Responses.StatusCodeResponses
// deleted responses
for code1 := range op1Responses {
if _, ok := op2Responses[code1]; !ok {
location := DifferenceLocation{URL: URLMethod2.Path, Method: URLMethod2.Method, Response: code1}
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: location, Code: DeletedResponse})
}
}
// Added updated Response Codes
for code2, op2Response := range op2Responses {
if op1Response, ok := op1Responses[code2]; ok {
op1Headers := op1Response.ResponseProps.Headers
headerRootNode := getNameOnlyDiffNode("Headers")
location := DifferenceLocation{URL: URLMethod2.Path, Method: URLMethod2.Method, Response: code2, Node: headerRootNode}
// Iterate Spec2 Headers looking for added and updated
for op2HeaderName, op2Header := range op2Response.ResponseProps.Headers {
if op1Header, ok := op1Headers[op2HeaderName]; ok {
sd.compareSimpleSchema(location.AddNode(getNameOnlyDiffNode(op2HeaderName)),
&op1Header.SimpleSchema,
&op2Header.SimpleSchema, false, false)
} else {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{
DifferenceLocation: location.AddNode(getNameOnlyDiffNode(op2HeaderName)),
Code: AddedResponseHeader})
}
}
for op1HeaderName := range op1Response.ResponseProps.Headers {
if _, ok := op2Response.ResponseProps.Headers[op1HeaderName]; !ok {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{
DifferenceLocation: location.AddNode(getNameOnlyDiffNode(op1HeaderName)),
Code: DeletedResponseHeader})
}
}
responseLocation := DifferenceLocation{URL: URLMethod2.Path, Method: URLMethod2.Method, Response: code2}
sd.compareDescripton(responseLocation, op1Response.Description, op2Response.Description)
if op1Response.Schema != nil {
sd.compareSchema(
DifferenceLocation{URL: URLMethod2.Path, Method: URLMethod2.Method, Response: code2},
op1Response.Schema,
op2Response.Schema, true, true)
}
} else {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{
DifferenceLocation: DifferenceLocation{URL: URLMethod2.Path, Method: URLMethod2.Method, Response: code2},
Code: AddedResponse})
}
}
}
}
}
func addTypeDiff(diffs []TypeDiff, diff TypeDiff) []TypeDiff {
if diff.Change != NoChangeDetected {
diffs = append(diffs, diff)
}
return diffs
}
// CheckToFromPrimitiveType check for diff to or from a primitive
func (sd *SpecAnalyser) CheckToFromPrimitiveType(diffs []TypeDiff, type1, type2 spec.SchemaProps) []TypeDiff {
type1IsPrimitive := len(type1.Type) > 0
type2IsPrimitive := len(type2.Type) > 0
// Primitive to Obj or Obj to Primitive
if type1IsPrimitive && !type2IsPrimitive {
return addTypeDiff(diffs, TypeDiff{Change: ChangedType, FromType: type1.Type[0], ToType: "obj"})
}
if !type1IsPrimitive && type2IsPrimitive {
return addTypeDiff(diffs, TypeDiff{Change: ChangedType, FromType: type2.Type[0], ToType: "obj"})
}
return diffs
}
// CheckToFromArrayType check for changes to or from an Array type
func (sd *SpecAnalyser) CheckToFromArrayType(diffs []TypeDiff, type1, type2 spec.SchemaProps) []TypeDiff {
// Single to Array or Array to Single
type1Array := type1.Type[0] == ArrayType
type2Array := type2.Type[0] == ArrayType
if type1Array && !type2Array {
return addTypeDiff(diffs, TypeDiff{Change: ChangedType, FromType: "obj", ToType: type2.Type[0]})
}
if !type1Array && type2Array {
return addTypeDiff(diffs, TypeDiff{Change: ChangedType, FromType: type1.Type[0], ToType: ArrayType})
}
if type1Array && type2Array {
// array
// TODO: Items??
diffs = addTypeDiff(diffs, compareIntValues("MaxItems", type1.MaxItems, type2.MaxItems, WidenedType, NarrowedType))
diffs = addTypeDiff(diffs, compareIntValues("MinItems", type1.MinItems, type2.MinItems, NarrowedType, WidenedType))
}
return diffs
}
// CheckStringTypeChanges checks for changes to or from a string type
func (sd *SpecAnalyser) CheckStringTypeChanges(diffs []TypeDiff, type1, type2 spec.SchemaProps) []TypeDiff {
// string changes
if type1.Type[0] == StringType &&
type2.Type[0] == StringType {
diffs = addTypeDiff(diffs, compareIntValues("MinLength", type1.MinLength, type2.MinLength, NarrowedType, WidenedType))
diffs = addTypeDiff(diffs, compareIntValues("MaxLength", type1.MinLength, type2.MinLength, WidenedType, NarrowedType))
if type1.Pattern != type2.Pattern {
diffs = addTypeDiff(diffs, TypeDiff{Change: ChangedType, Description: fmt.Sprintf("Pattern Changed:%s->%s", type1.Pattern, type2.Pattern)})
}
if type1.Type[0] == StringType {
if len(type1.Enum) > 0 {
enumDiffs := sd.compareEnums(type1.Enum, type2.Enum)
diffs = append(diffs, enumDiffs...)
}
}
}
return diffs
}
// CheckNumericTypeChanges checks for changes to or from a numeric type
func (sd *SpecAnalyser) CheckNumericTypeChanges(diffs []TypeDiff, type1, type2 spec.SchemaProps) []TypeDiff {
// Number
_, type1IsNumeric := numberWideness[type1.Type[0]]
_, type2IsNumeric := numberWideness[type2.Type[0]]
if type1IsNumeric && type2IsNumeric {
diffs = addTypeDiff(diffs, compareFloatValues("Maximum", type1.Maximum, type2.Maximum, WidenedType, NarrowedType))
diffs = addTypeDiff(diffs, compareFloatValues("Minimum", type1.Minimum, type2.Minimum, NarrowedType, WidenedType))
if type1.ExclusiveMaximum && !type2.ExclusiveMaximum {
diffs = addTypeDiff(diffs, TypeDiff{Change: WidenedType, Description: fmt.Sprintf("Exclusive Maximum Removed:%v->%v", type1.ExclusiveMaximum, type2.ExclusiveMaximum)})
}
if !type1.ExclusiveMaximum && type2.ExclusiveMaximum {
diffs = addTypeDiff(diffs, TypeDiff{Change: NarrowedType, Description: fmt.Sprintf("Exclusive Maximum Added:%v->%v", type1.ExclusiveMaximum, type2.ExclusiveMaximum)})
}
if type1.ExclusiveMinimum && !type2.ExclusiveMinimum {
diffs = addTypeDiff(diffs, TypeDiff{Change: WidenedType, Description: fmt.Sprintf("Exclusive Minimum Removed:%v->%v", type1.ExclusiveMaximum, type2.ExclusiveMaximum)})
}
if !type1.ExclusiveMinimum && type2.ExclusiveMinimum {
diffs = addTypeDiff(diffs, TypeDiff{Change: NarrowedType, Description: fmt.Sprintf("Exclusive Minimum Added:%v->%v", type1.ExclusiveMinimum, type2.ExclusiveMinimum)})
}
}
return diffs
}
// CompareTypes computes type specific property diffs
func (sd *SpecAnalyser) CompareTypes(type1, type2 spec.SchemaProps) []TypeDiff {
diffs := []TypeDiff{}
diffs = sd.CheckToFromPrimitiveType(diffs, type1, type2)
if len(diffs) > 0 {
return diffs
}
diffs = sd.CheckToFromArrayType(diffs, type1, type2)
if len(diffs) > 0 {
return diffs
}
// check type hierarchy change eg string -> integer = NarrowedChange
//Type
//Format
if type1.Type[0] != type2.Type[0] ||
type1.Format != type2.Format {
diff := getTypeHierarchyChange(primitiveTypeString(type1.Type[0], type1.Format), primitiveTypeString(type2.Type[0], type2.Format))
diffs = addTypeDiff(diffs, diff)
}
diffs = sd.CheckStringTypeChanges(diffs, type1, type2)
if len(diffs) > 0 {
return diffs
}
diffs = sd.CheckNumericTypeChanges(diffs, type1, type2)
if len(diffs) > 0 {
return diffs
}
return diffs
}
func (sd *SpecAnalyser) compareParams(urlMethod URLMethod, location string, name string, param1, param2 spec.Parameter) {
diffLocation := DifferenceLocation{URL: urlMethod.Path, Method: urlMethod.Method}
childLocation := diffLocation.AddNode(getNameOnlyDiffNode(strings.Title(location)))
paramLocation := diffLocation.AddNode(getNameOnlyDiffNode(name))
sd.compareDescripton(paramLocation, param1.Description, param2.Description)
if param1.Schema != nil && param2.Schema != nil {
childLocation = childLocation.AddNode(getSchemaDiffNode(name, param2.Schema))
sd.compareSchema(childLocation, param1.Schema, param2.Schema, param1.Required, param2.Required)
}
diffs := sd.CompareTypes(forParam(param1), forParam(param2))
childLocation = childLocation.AddNode(getSchemaDiffNode(name, param2.Schema))
for _, eachDiff := range diffs {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{
DifferenceLocation: childLocation,
Code: eachDiff.Change,
DiffInfo: eachDiff.Description})
}
if param1.Required != param2.Required {
code := ChangedRequiredToOptionalParam
if param2.Required {
code = ChangedOptionalToRequiredParam
}
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: childLocation, Code: code})
}
}
func (sd *SpecAnalyser) compareSimpleSchema(location DifferenceLocation, schema1, schema2 *spec.SimpleSchema, required1, required2 bool) {
if schema1 == nil || schema2 == nil {
return
}
if schema1.Type == ArrayType {
refSchema1 := schema1.Items.SimpleSchema
refSchema2 := schema2.Items.SimpleSchema
childLocation := location.AddNode(getSimpleSchemaDiffNode("", schema1))
sd.compareSimpleSchema(childLocation, &refSchema1, &refSchema2, required1, required2)
return
}
if required1 != required2 {
code := AddedRequiredProperty
if required1 {
code = ChangedRequiredToOptional
}
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: location, Code: code})
}
}
func (sd *SpecAnalyser) compareDescripton(location DifferenceLocation, desc1, desc2 string) {
if desc1 != desc2 {
code := ChangedDescripton
if len(desc1) > 0 {
code = DeletedDescripton
} else if len(desc2) > 0 {
code = AddedDescripton
}
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: location, Code: code})
}
}
func (sd *SpecAnalyser) compareSchema(location DifferenceLocation, schema1, schema2 *spec.Schema, required1, required2 bool) {
if schema1 == nil || schema2 == nil {
return
}
sd.compareDescripton(location, schema1.Description, schema2.Description)
if len(schema1.Type) == 0 {
refSchema1, definition1 := sd.schemaFromRef(schema1, &sd.Definitions1)
refSchema2, definition2 := sd.schemaFromRef(schema2, &sd.Definitions2)
if len(definition1) > 0 {
info := fmt.Sprintf("[%s -> %s]", definition1, definition2)
if definition1 != definition2 {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: location,
Code: ChangedType,
DiffInfo: info,
})
}
sd.compareSchema(location, refSchema1, refSchema2, required1, required2)
return
}
} else {
if schema1.Type[0] == ArrayType {
refSchema1, definition1 := sd.schemaFromRef(schema1.Items.Schema, &sd.Definitions1)
refSchema2, _ := sd.schemaFromRef(schema2.Items.Schema, &sd.Definitions2)
if len(definition1) > 0 {
childLocation := location.AddNode(getSchemaDiffNode("", schema1))
sd.compareSchema(childLocation, refSchema1, refSchema2, required1, required2)
return
}
}
diffs := sd.CompareTypes(schema1.SchemaProps, schema2.SchemaProps)
for _, eachTypeDiff := range diffs {
if eachTypeDiff.Change != NoChangeDetected {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: location, Code: eachTypeDiff.Change, DiffInfo: eachTypeDiff.Description})
}
}
}
if required1 != required2 {
code := AddedRequiredProperty
if required1 {
code = ChangedRequiredToOptional
}
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: location, Code: code})
}
requiredProps2 := sliceToStrMap(schema2.Required)
requiredProps1 := sliceToStrMap(schema1.Required)
schema1Props := sd.propertiesFor(schema1, &sd.Definitions1)
schema2Props := sd.propertiesFor(schema2, &sd.Definitions2)
// find deleted and changed properties
for eachProp1Name, eachProp1 := range schema1Props {
eachProp1 := eachProp1
_, required1 := requiredProps1[eachProp1Name]
_, required2 := requiredProps2[eachProp1Name]
childLoc := sd.addChildDiffNode(location, eachProp1Name, &eachProp1)
if eachProp2, ok := schema2Props[eachProp1Name]; ok {
sd.compareSchema(childLoc, &eachProp1, &eachProp2, required1, required2)
sd.compareDescripton(childLoc, eachProp1.Description, eachProp2.Description)
} else {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: childLoc, Code: DeletedProperty})
}
}
// find added properties
for eachProp2Name, eachProp2 := range schema2.Properties {
eachProp2 := eachProp2
if _, ok := schema1.Properties[eachProp2Name]; !ok {
childLoc := sd.addChildDiffNode(location, eachProp2Name, &eachProp2)
_, required2 := requiredProps2[eachProp2Name]
code := AddedProperty
if required2 {
code = AddedRequiredProperty
}
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: childLoc, Code: code})
}
}
}
func (sd *SpecAnalyser) addChildDiffNode(location DifferenceLocation, propName string, propSchema *spec.Schema) DifferenceLocation {
newLoc := location
if newLoc.Node != nil {
newLoc.Node = newLoc.Node.Copy()
}
childNode := sd.fromSchemaProps(propName, &propSchema.SchemaProps)
if newLoc.Node != nil {
newLoc.Node.AddLeafNode(&childNode)
} else {
newLoc.Node = &childNode
}
return newLoc
}
func (sd *SpecAnalyser) fromSchemaProps(fieldName string, props *spec.SchemaProps) Node {
node := Node{}
node.IsArray = props.Type[0] == ArrayType
if !node.IsArray {
node.TypeName = props.Type[0]
}
node.Field = fieldName
return node
}
func (sd *SpecAnalyser) compareEnums(left, right []interface{}) []TypeDiff {
diffs := []TypeDiff{}
leftStrs := []string{}
rightStrs := []string{}
for _, eachLeft := range left {
leftStrs = append(leftStrs, fmt.Sprintf("%v", eachLeft))
}
for _, eachRight := range right {
rightStrs = append(rightStrs, fmt.Sprintf("%v", eachRight))
}
added, deleted, _ := FromStringArray(leftStrs).DiffsTo(rightStrs)
if len(added) > 0 {
typeChange := strings.Join(added, ",")
diffs = append(diffs, TypeDiff{Change: AddedEnumValue, Description: typeChange})
}
if len(deleted) > 0 {
typeChange := strings.Join(deleted, ",")
diffs = append(diffs, TypeDiff{Change: DeletedEnumValue, Description: typeChange})
}
return diffs
}
func (sd *SpecAnalyser) findAddedEndpoints() {
for URLMethod := range sd.urlMethods2 {
if _, ok := sd.urlMethods1[URLMethod]; !ok {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: DifferenceLocation{URL: URLMethod.Path, Method: URLMethod.Method}, Code: AddedEndpoint})
}
}
}
func (sd *SpecAnalyser) findDeletedEndpoints() {
for eachURLMethod, operation1 := range sd.urlMethods1 {
code := DeletedEndpoint
if (operation1.ParentPathItem.Options != nil && operation1.ParentPathItem.Options.Deprecated) ||
(operation1.Operation.Deprecated) {
code = DeletedDeprecatedEndpoint
}
if _, ok := sd.urlMethods2[eachURLMethod]; !ok {
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: DifferenceLocation{URL: eachURLMethod.Path, Method: eachURLMethod.Method}, Code: code})
}
}
}
func (sd *SpecAnalyser) analyseMetaDataProperty(item1, item2 string, codeIfDiff SpecChangeCode, compatIfDiff Compatibility) {
if item1 != item2 {
diffSpec := fmt.Sprintf("%s -> %s", item1, item2)
sd.Diffs = sd.Diffs.addDiff(SpecDifference{DifferenceLocation: DifferenceLocation{Node: &Node{Field: "Spec Metadata"}}, Code: codeIfDiff, Compatibility: compatIfDiff, DiffInfo: diffSpec})
}
}
func (sd *SpecAnalyser) schemaFromRef(schema *spec.Schema, defns *spec.Definitions) (actualSchema *spec.Schema, definitionName string) {
ref := schema.Ref
url := ref.GetURL()
if url == nil {
return schema, ""
}
fragmentParts := strings.Split(url.Fragment, "/")
numParts := len(fragmentParts)
if numParts == 0 {
return schema, ""
}
definitionName = fragmentParts[numParts-1]
foundSchema, ok := (*defns)[definitionName]
if !ok {
return nil, definitionName
}
actualSchema = &foundSchema
return
}
func (sd *SpecAnalyser) propertiesFor(schema *spec.Schema, defns *spec.Definitions) map[string]spec.Schema {
schemaFromRef, _ := sd.schemaFromRef(schema, defns)
schema = schemaFromRef
props := map[string]spec.Schema{}
if schema.Properties != nil {
for name, prop := range schema.Properties {
prop := prop
eachProp, _ := sd.schemaFromRef(&prop, defns)
props[name] = *eachProp
}
}
for _, eachAllOf := range schema.AllOf {
eachAllOf := eachAllOf
eachAllOfActual, _ := sd.schemaFromRef(&eachAllOf, defns)
for name, prop := range eachAllOfActual.Properties {
prop := prop
eachProp, _ := sd.schemaFromRef(&prop, defns)
props[name] = *eachProp
}
}
return props
}