diff --git a/data/delta/base.yaml b/data/delta/base.yaml new file mode 100644 index 00000000..821d8006 --- /dev/null +++ b/data/delta/base.yaml @@ -0,0 +1,29 @@ +openapi: 3.0.0 +info: + title: Sample API + version: 1.0.0 +paths: + /users: + get: + summary: List all users + responses: + '200': + description: A list of users. + /users/{userId}: + get: + summary: Get a single user by ID + parameters: + - name: userId + in: path + required: true + schema: + type: string + responses: + '200': + description: A single user. + /products: + get: + summary: List all products + responses: + '200': + description: A list of products. diff --git a/data/delta/revision.yaml b/data/delta/revision.yaml new file mode 100644 index 00000000..b1a4e0d6 --- /dev/null +++ b/data/delta/revision.yaml @@ -0,0 +1,17 @@ +openapi: 3.0.0 +info: + title: Sample API + version: 1.0.0 +paths: + /users: + get: + summary: List all users + responses: + '200': + description: A list of users. + /products: + post: + summary: List all products + responses: + '200': + description: A list of products. diff --git a/delta/delta_test.go b/delta/delta_test.go index a7b897f5..96a4dba3 100644 --- a/delta/delta_test.go +++ b/delta/delta_test.go @@ -193,10 +193,12 @@ func TestSymmetric(t *testing.T) { s2, err := loader.LoadFromFile(pair.Y) require.NoError(t, err) - d1, err := diff.Get(diff.NewConfig(), s1, s2) + config := diff.NewConfig().WithUnchanged() + + d1, err := diff.Get(config, s1, s2) require.NoError(t, err) - d2, err := diff.Get(diff.NewConfig(), s2, s1) + d2, err := diff.Get(config, s2, s1) require.NoError(t, err) require.Equal(t, delta.Get(false, d1), delta.Get(false, d2), pair) @@ -215,11 +217,13 @@ func TestAsymmetric(t *testing.T) { s2, err := loader.LoadFromFile(pair.Y) require.NoError(t, err) - d1, err := diff.Get(diff.NewConfig(), s1, s2) + config := diff.NewConfig().WithUnchanged() + + d1, err := diff.Get(config, s1, s2) require.NoError(t, err) asymmetric1 := delta.Get(true, d1) - d2, err := diff.Get(diff.NewConfig(), s2, s1) + d2, err := diff.Get(config, s2, s1) require.NoError(t, err) asymmetric2 := delta.Get(true, d2) @@ -228,3 +232,20 @@ func TestAsymmetric(t *testing.T) { require.Equal(t, asymmetric1+asymmetric2, symmetric, pair) } } + +func TestUnchanged(t *testing.T) { + + loader := openapi3.NewLoader() + s1, err := loader.LoadFromFile("../data/delta/base.yaml") + require.NoError(t, err) + + s2, err := loader.LoadFromFile("../data/delta/revision.yaml") + require.NoError(t, err) + + config := diff.NewConfig().WithUnchanged() + + d, err := diff.Get(config, s1, s2) + require.NoError(t, err) + + require.Equal(t, 0.75, delta.Get(false, d)) +} diff --git a/diff/config.go b/diff/config.go index e3052c8e..981fc888 100644 --- a/diff/config.go +++ b/diff/config.go @@ -15,6 +15,7 @@ type Config struct { PathStripPrefixRevision string ExcludeElements utils.StringSet IncludePathParams bool + Unchanged bool } const ( @@ -79,3 +80,8 @@ func (config *Config) WithCheckBreaking() *Config { return config } + +func (config *Config) WithUnchanged() *Config { + config.Unchanged = true + return config +} diff --git a/diff/diff_test.go b/diff/diff_test.go index 32be3376..5a204f2e 100644 --- a/diff/diff_test.go +++ b/diff/diff_test.go @@ -85,10 +85,9 @@ func TestDiff_ModifiedOperation(t *testing.T) { require.NoError(t, err) require.Equal(t, &diff.OperationsDiff{ - Added: utils.StringList{"GET"}, - Deleted: utils.StringList{"POST"}, - Modified: diff.ModifiedOperations{}, - Unchanged: utils.StringList{}, + Added: utils.StringList{"GET"}, + Deleted: utils.StringList{"POST"}, + Modified: diff.ModifiedOperations{}, }, d.PathsDiff.Modified["/api/test"].OperationsDiff) } diff --git a/diff/endpoints_diff.go b/diff/endpoints_diff.go index aaccbfdb..2f15f7db 100644 --- a/diff/endpoints_diff.go +++ b/diff/endpoints_diff.go @@ -33,7 +33,8 @@ func (diff *EndpointsDiff) Empty() bool { return len(diff.Added) == 0 && len(diff.Deleted) == 0 && - len(diff.Modified) == 0 + len(diff.Modified) == 0 && + len(diff.Unchanged) == 0 } func newEndpointsDiff() *EndpointsDiff { @@ -60,6 +61,10 @@ func getEndpointsDiff(config *Config, state *state, paths1, paths2 *openapi3.Pat return nil, err } + if !config.Unchanged { + diff.Unchanged = nil + } + if diff.Empty() { return nil, nil } @@ -78,18 +83,18 @@ func getEndpointsDiffInternal(config *Config, state *state, paths1, paths2 *open for path, pathItem := range addedPaths.Map() { for method := range pathItem.Operations() { - result.addAddedPath(path, method) + result.addAddedEndpoint(path, method) } } for path, pathItem := range deletedPaths.Map() { for method := range pathItem.Operations() { - result.addDeletedPath(path, method) + result.addDeletedEndpoint(path, method) } } for path, pathItemPair := range otherPaths { - if err := result.addModifiedPaths(config, state, path, pathItemPair); err != nil { + if err := result.addModifiedEndpoints(config, state, path, pathItemPair); err != nil { return nil, err } } @@ -97,55 +102,55 @@ func getEndpointsDiffInternal(config *Config, state *state, paths1, paths2 *open return result, nil } -func (diff *EndpointsDiff) addAddedPath(path string, method string) { +func (diff *EndpointsDiff) addAddedEndpoint(path string, method string) { diff.Added = append(diff.Added, Endpoint{ Method: method, Path: path, }) } -func (diff *EndpointsDiff) addDeletedPath(path string, method string) { +func (diff *EndpointsDiff) addDeletedEndpoint(path string, method string) { diff.Deleted = append(diff.Deleted, Endpoint{ Method: method, Path: path, }) } -func (diff *EndpointsDiff) addUnchangedPath(path string, method string) { +func (diff *EndpointsDiff) addUnchangedEndpoint(path string, method string) { diff.Unchanged = append(diff.Unchanged, Endpoint{ Method: method, Path: path, }) } -func (diff *EndpointsDiff) addModifiedPaths(config *Config, state *state, path string, pathItemPair *pathItemPair) error { +func (diff *EndpointsDiff) addModifiedEndpoints(config *Config, state *state, path string, pathItemPair *pathItemPair) error { - pathDiff, err := getPathDiff(config, state, pathItemPair) + operationsDiff, err := getOperationsDiff(config, state, pathItemPair) if err != nil { return err } - if pathDiff.Empty() || pathDiff.OperationsDiff.Empty() { + if operationsDiff.Empty() { return nil } - for _, method := range pathDiff.OperationsDiff.Added { - diff.addAddedPath(path, method) + for _, method := range operationsDiff.Added { + diff.addAddedEndpoint(path, method) } - for _, method := range pathDiff.OperationsDiff.Deleted { - diff.addDeletedPath(path, method) + for _, method := range operationsDiff.Deleted { + diff.addDeletedEndpoint(path, method) } - for method, methodDiff := range pathDiff.OperationsDiff.Modified { + for method, methodDiff := range operationsDiff.Modified { diff.Modified[Endpoint{ Method: method, Path: path, }] = methodDiff } - for _, method := range pathDiff.OperationsDiff.Unchanged { - diff.addUnchangedPath(path, method) + for _, method := range operationsDiff.Unchanged { + diff.addUnchangedEndpoint(path, method) } return nil diff --git a/diff/operations_diff.go b/diff/operations_diff.go index e053c89c..832dca67 100644 --- a/diff/operations_diff.go +++ b/diff/operations_diff.go @@ -25,7 +25,8 @@ func (operationsDiff *OperationsDiff) Empty() bool { return len(operationsDiff.Added) == 0 && len(operationsDiff.Deleted) == 0 && - len(operationsDiff.Modified) == 0 + len(operationsDiff.Modified) == 0 && + len(operationsDiff.Unchanged) == 0 } func newOperationsDiff() *OperationsDiff { @@ -51,6 +52,10 @@ func getOperationsDiff(config *Config, state *state, pathItemPair *pathItemPair) return nil, err } + if !config.Unchanged { + diff.Unchanged = nil + } + if diff.Empty() { return nil, nil } diff --git a/diff/parameters_diff_by_location.go b/diff/parameters_diff_by_location.go index 55ee44e1..cb12c398 100644 --- a/diff/parameters_diff_by_location.go +++ b/diff/parameters_diff_by_location.go @@ -23,7 +23,8 @@ func (diff *ParametersDiffByLocation) Empty() bool { return len(diff.Added) == 0 && len(diff.Deleted) == 0 && - len(diff.Modified) == 0 + len(diff.Modified) == 0 && + len(diff.Unchanged) == 0 } // ParamLocations are the four possible locations of parameters: path, query, header or cookie @@ -107,6 +108,10 @@ func getParametersDiffByLocation(config *Config, state *state, params1, params2 return nil, err } + if !config.Unchanged { + diff.Unchanged = nil + } + if diff.Empty() { return nil, nil } diff --git a/diff/responses_diff.go b/diff/responses_diff.go index 4d6a678d..8f66e4e5 100644 --- a/diff/responses_diff.go +++ b/diff/responses_diff.go @@ -23,7 +23,8 @@ func (responsesDiff *ResponsesDiff) Empty() bool { return len(responsesDiff.Added) == 0 && len(responsesDiff.Deleted) == 0 && - len(responsesDiff.Modified) == 0 + len(responsesDiff.Modified) == 0 && + len(responsesDiff.Unchanged) == 0 } // ModifiedResponses is map of response values to their respective diffs @@ -48,6 +49,10 @@ func getResponsesDiff(config *Config, state *state, responses1, responses2 *open return nil, err } + if !config.Unchanged { + diff.Unchanged = nil + } + if diff.Empty() { return nil, nil } @@ -75,6 +80,7 @@ func getResponsesDiffInternal(config *Config, state *state, responses1, response if err != nil { return nil, err } + if diff.Empty() { result.Unchanged = append(result.Unchanged, responseValue1) } else { diff --git a/internal/changelog_flags.go b/internal/changelog_flags.go index ffb3f616..c6dba742 100644 --- a/internal/changelog_flags.go +++ b/internal/changelog_flags.go @@ -30,6 +30,7 @@ type ChangelogFlags struct { deprecationDaysBeta int deprecationDaysStable int color string + unchanged bool } func (flags *ChangelogFlags) toConfig() *diff.Config { @@ -41,6 +42,7 @@ func (flags *ChangelogFlags) toConfig() *diff.Config { config.PathStripPrefixBase = flags.stripPrefixBase config.PathStripPrefixRevision = flags.stripPrefixRevision config.IncludePathParams = flags.includePathParams + config.Unchanged = flags.unchanged return config } @@ -125,6 +127,10 @@ func (flags *ChangelogFlags) setRevision(source *load.Source) { flags.revision = source } +func (flags *ChangelogFlags) setUnchanged(unchanged bool) { + flags.unchanged = unchanged +} + func (flags *ChangelogFlags) addExcludeElements(element string) { flags.excludeElements = append(flags.excludeElements, element) } diff --git a/internal/delta.go b/internal/delta.go index 9e980bef..6a4917d5 100644 --- a/internal/delta.go +++ b/internal/delta.go @@ -30,6 +30,7 @@ func getDeltaCmd() *cobra.Command { func runDelta(flags Flags, stdout io.Writer) (bool, *ReturnError) { openapi3.CircularReferenceCounter = flags.getCircularReferenceCounter() + flags.setUnchanged(true) diffResult, err := calcDiff(flags) if err != nil { diff --git a/internal/diff_flags.go b/internal/diff_flags.go index 27d30cc6..7f4546e0 100644 --- a/internal/diff_flags.go +++ b/internal/diff_flags.go @@ -23,6 +23,7 @@ type DiffFlags struct { circularReferenceCounter int includePathParams bool excludeElements []string + unchanged bool } func (flags *DiffFlags) toConfig() *diff.Config { @@ -34,6 +35,7 @@ func (flags *DiffFlags) toConfig() *diff.Config { config.PathStripPrefixBase = flags.stripPrefixBase config.PathStripPrefixRevision = flags.stripPrefixRevision config.IncludePathParams = flags.includePathParams + config.Unchanged = flags.unchanged return config } @@ -118,6 +120,10 @@ func (flags *DiffFlags) setRevision(source *load.Source) { flags.revision = source } +func (flags *DiffFlags) setUnchanged(unchanged bool) { + flags.unchanged = unchanged +} + func (flags *DiffFlags) addExcludeElements(element string) { flags.excludeElements = append(flags.excludeElements, element) } diff --git a/internal/flags.go b/internal/flags.go index dc267e2e..069bc024 100644 --- a/internal/flags.go +++ b/internal/flags.go @@ -29,6 +29,7 @@ type Flags interface { setBase(source *load.Source) setRevision(source *load.Source) + setUnchanged(bool) addExcludeElements(string)