From 5369849295d3484c9789a3393ae49a72aacf76fe Mon Sep 17 00:00:00 2001 From: Reuven Date: Wed, 3 Apr 2024 17:35:08 +0300 Subject: [PATCH] diff extensions --- ...rameter-x-extensible-enum-value-removed.go | 4 +- ...roperty-x-extensible-enum-value-removed.go | 4 +- data/extensions/base.yaml | 31 +++++++++++++++ data/extensions/revision.yaml | 31 +++++++++++++++ diff/config.go | 8 ++++ diff/diff_test.go | 24 ++++++++++++ diff/interface_map_diff.go | 19 ++++----- diff/modified_interfaces.go | 39 +++++++++++++++++++ go.mod | 5 +++ go.sum | 11 ++++++ 10 files changed, 161 insertions(+), 15 deletions(-) create mode 100644 data/extensions/base.yaml create mode 100644 data/extensions/revision.yaml create mode 100644 diff/modified_interfaces.go diff --git a/checker/check-request-parameter-x-extensible-enum-value-removed.go b/checker/check-request-parameter-x-extensible-enum-value-removed.go index c50de9ab..29f09046 100644 --- a/checker/check-request-parameter-x-extensible-enum-value-removed.go +++ b/checker/check-request-parameter-x-extensible-enum-value-removed.go @@ -45,11 +45,11 @@ func RequestParameterXExtensibleEnumValueRemovedCheck(diffReport *diff.Diff, ope if paramItem.SchemaDiff.ExtensionsDiff.Modified[diff.XExtensibleEnumExtension] == nil { continue } - from, ok := paramItem.SchemaDiff.ExtensionsDiff.Modified[diff.XExtensibleEnumExtension].From.(json.RawMessage) + from, ok := diff.GetJsonOrigValue(paramItem.SchemaDiff.ExtensionsDiff.Modified[diff.XExtensibleEnumExtension]) if !ok { continue } - to, ok := paramItem.SchemaDiff.ExtensionsDiff.Modified[diff.XExtensibleEnumExtension].To.(json.RawMessage) + to, ok := diff.GetJsonNewValue(paramItem.SchemaDiff.ExtensionsDiff.Modified[diff.XExtensibleEnumExtension]) if !ok { continue } diff --git a/checker/check-request-property-x-extensible-enum-value-removed.go b/checker/check-request-property-x-extensible-enum-value-removed.go index fdf8b25c..1bd6d28a 100644 --- a/checker/check-request-property-x-extensible-enum-value-removed.go +++ b/checker/check-request-property-x-extensible-enum-value-removed.go @@ -45,11 +45,11 @@ func RequestPropertyXExtensibleEnumValueRemovedCheck(diffReport *diff.Diff, oper if propertyDiff.ExtensionsDiff.Modified[diff.XExtensibleEnumExtension] == nil { return } - from, ok := propertyDiff.ExtensionsDiff.Modified[diff.XExtensibleEnumExtension].From.(json.RawMessage) + from, ok := diff.GetJsonOrigValue(propertyDiff.ExtensionsDiff.Modified[diff.XExtensibleEnumExtension]) if !ok { return } - to, ok := propertyDiff.ExtensionsDiff.Modified[diff.XExtensibleEnumExtension].To.(json.RawMessage) + to, ok := diff.GetJsonOrigValue(propertyDiff.ExtensionsDiff.Modified[diff.XExtensibleEnumExtension]) if !ok { return } diff --git a/data/extensions/base.yaml b/data/extensions/base.yaml new file mode 100644 index 00000000..f88797b2 --- /dev/null +++ b/data/extensions/base.yaml @@ -0,0 +1,31 @@ +info: + title: Tufin + version: 1.0.0 +openapi: 3.0.3 +paths: + /example/callback: + post: + responses: + '200': + description: 200 response + content: + application/json: + schema: + $ref: '#/components/schemas/Empty' + x-amazon-apigateway-integration: + type: http_proxy + connectionId: ${vpc_link_id} + httpMethod: POST + uri: http://api.example.com/v1/example/callback + responses: + default: + statusCode: '201' + passthroughBehavior: when_no_match + connectionType: VPC_LINK +components: + schemas: + Empty: + type: object + title: Empty Schema + properties: {} + required: [] diff --git a/data/extensions/revision.yaml b/data/extensions/revision.yaml new file mode 100644 index 00000000..0f0d57c9 --- /dev/null +++ b/data/extensions/revision.yaml @@ -0,0 +1,31 @@ +info: + title: Tufin + version: 1.0.0 +openapi: 3.0.3 +paths: + /example/callback: + post: + responses: + '200': + description: 200 response + content: + application/json: + schema: + $ref: '#/components/schemas/Empty' + x-amazon-apigateway-integration: + type: http_proxy + connectionId: ${vpc_link_id} + httpMethod: POST + uri: http://api.example.com/v1/example/calllllllllback + responses: + default: + statusCode: '200' + passthroughBehavior: when_no_match + connectionType: VPC_LINK +components: + schemas: + Empty: + type: object + title: Empty Schema + properties: {} + required: [] \ No newline at end of file diff --git a/diff/config.go b/diff/config.go index e3052c8e..46f34163 100644 --- a/diff/config.go +++ b/diff/config.go @@ -79,3 +79,11 @@ func (config *Config) WithCheckBreaking() *Config { return config } + +func (config *Config) WithExtensions(extensions ...string) *Config { + for _, extension := range extensions { + config.IncludeExtensions.Add(extension) + } + + return config +} diff --git a/diff/diff_test.go b/diff/diff_test.go index 32be3376..4ca2733e 100644 --- a/diff/diff_test.go +++ b/diff/diff_test.go @@ -902,3 +902,27 @@ func TestDiff_DifferentComponentSameHeader(t *testing.T) { require.NoError(t, err) require.Empty(t, d) } + +func TestDiff_Extensions(t *testing.T) { + loader := openapi3.NewLoader() + + s1, err := loader.LoadFromFile("../data/extensions/base.yaml") + require.NoError(t, err) + + s2, err := loader.LoadFromFile("../data/extensions/revision.yaml") + require.NoError(t, err) + + extension := "x-amazon-apigateway-integration" + d, _, err := diff.GetWithOperationsSourcesMap(diff.NewConfig().WithExtensions(extension), + &load.SpecInfo{ + Spec: s1, + }, + &load.SpecInfo{ + Spec: s2, + }) + require.NoError(t, err) + dd := d.PathsDiff.Modified["/example/callback"].OperationsDiff.Modified["POST"].ExtensionsDiff.Modified[extension] + require.Len(t, dd, 2) + require.Equal(t, "{\"value\":\"200\",\"op\":\"replace\",\"path\":\"/responses/default/statusCode\"}", dd[0].String()) + require.Equal(t, "{\"value\":\"http://api.example.com/v1/example/calllllllllback\",\"op\":\"replace\",\"path\":\"/uri\"}", dd[1].String()) +} diff --git a/diff/interface_map_diff.go b/diff/interface_map_diff.go index 63cf1506..db134097 100644 --- a/diff/interface_map_diff.go +++ b/diff/interface_map_diff.go @@ -1,6 +1,9 @@ package diff -import "github.com/tufin/oasdiff/utils" +import ( + "github.com/tufin/oasdiff/utils" + "github.com/wI2L/jsondiff" +) // InterfaceMap is a map of string to interface type InterfaceMap map[string]interface{} @@ -12,14 +15,6 @@ type InterfaceMapDiff struct { Modified ModifiedInterfaces `json:"modified,omitempty" yaml:"modified,omitempty"` } -// ModifiedInterfaces is map of interface names to their respective diffs -type ModifiedInterfaces map[string]*ValueDiff - -// Empty indicates whether a change was found in this element -func (modifiedInterfaces ModifiedInterfaces) Empty() bool { - return len(modifiedInterfaces) == 0 -} - // Empty indicates whether a change was found in this element func (diff *InterfaceMapDiff) Empty() bool { if diff == nil { @@ -56,9 +51,11 @@ func getInterfaceMapDiffInternal(map1, map2 InterfaceMap, filter utils.StringSet for name1, interface1 := range map1 { if _, ok := filter[name1]; ok { if interface2, ok := map2[name1]; ok { - if diff := getValueDiff(interface1, interface2); !diff.Empty() { - result.Modified[name1] = diff + patch, err := jsondiff.Compare(interface1, interface2) + if err != nil { + // TODO: handle error } + result.Modified[name1] = patch } else { result.Deleted = append(result.Deleted, name1) } diff --git a/diff/modified_interfaces.go b/diff/modified_interfaces.go new file mode 100644 index 00000000..0c6a61f6 --- /dev/null +++ b/diff/modified_interfaces.go @@ -0,0 +1,39 @@ +package diff + +import ( + "encoding/json" + + "github.com/wI2L/jsondiff" +) + +// ModifiedInterfaces is map of interface names to their respective diffs +type ModifiedInterfaces map[string]jsondiff.Patch + +// Empty indicates whether a change was found in this element +func (modifiedInterfaces ModifiedInterfaces) Empty() bool { + return len(modifiedInterfaces) == 0 +} + +// GetJsonOrigValue returns the original value of the diff, only if there is exactly one diff +func GetJsonOrigValue(patch jsondiff.Patch) (json.RawMessage, bool) { + if len(patch) != 1 { + return nil, false + } + result, ok := patch[0].OldValue.(json.RawMessage) + if !ok { + return nil, false + } + return result, true +} + +// GetJsonOrigValue returns the new value of the diff, only if there is exactly one diff +func GetJsonNewValue(patch jsondiff.Patch) (json.RawMessage, bool) { + if len(patch) != 1 { + return nil, false + } + result, ok := patch[0].Value.(json.RawMessage) + if !ok { + return nil, false + } + return result, true +} diff --git a/go.mod b/go.mod index 93876538..1c818f50 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,10 @@ require ( github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/tidwall/gjson v1.17.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/sjson v1.2.5 // indirect golang.org/x/sys v0.18.0 // indirect ) @@ -37,4 +41,5 @@ require ( github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/cobra v1.8.0 + github.com/wI2L/jsondiff v0.5.1 ) diff --git a/go.sum b/go.sum index 928a89d3..67558d98 100644 --- a/go.sum +++ b/go.sum @@ -55,8 +55,19 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/wI2L/jsondiff v0.5.1 h1:xS4zYUspH4U3IB0Lwo9+jv+MSRJSWMF87Y4BpDbFMHo= +github.com/wI2L/jsondiff v0.5.1/go.mod h1:qqG6hnK0Lsrz2BpIVCxWiK9ItsBCpIZQiv0izJjOZ9s= github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc= github.com/yargevad/filepathx v1.0.0/go.mod h1:BprfX/gpYNJHJfc35GjRRpVcwWXS89gGulUIU5tK3tA= github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=