diff --git a/cmd/registry-experimental/breaking-change-detector/breaking_change_detector.go b/cmd/registry-experimental/breaking-change-detector/breaking_change_detector.go new file mode 100644 index 00000000..45b90adc --- /dev/null +++ b/cmd/registry-experimental/breaking-change-detector/breaking_change_detector.go @@ -0,0 +1,185 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package breakingchangedetector + +import ( + "regexp" + + "github.com/apigee/registry-experimental/rpc" +) + +type detectionPattern struct { + PositiveMatchRegex *regexp.Regexp + NegativeMatchRegex *regexp.Regexp +} + +var ( + unsafeAdds = []detectionPattern{ + { + PositiveMatchRegex: regexp.MustCompile("(components.)+(.|)+(schemas)+(.)+(required)"), + }, + } + + unsafeDeletes = []detectionPattern{ + { + PositiveMatchRegex: regexp.MustCompile("(components.)+(.|)+(schemas)+(.)"), + NegativeMatchRegex: regexp.MustCompile("(components.)+(.|)+(schemas)+(.)+(required)"), + }, + { + PositiveMatchRegex: regexp.MustCompile("(paths.)+(.|)"), + NegativeMatchRegex: regexp.MustCompile("((paths.)+(.|)+(tags))+(|)+((paths.)+(.|)+(description))"), + }, + } + + unsafeMods = []detectionPattern{ + { + PositiveMatchRegex: regexp.MustCompile("(components.)+(.|)+(schemas)+(.|)+(type)"), + }, + { + PositiveMatchRegex: regexp.MustCompile("(paths.)+(.|)+(type)"), + NegativeMatchRegex: regexp.MustCompile("((paths.)+(.|)+(tags))+(|)+((paths.)+(.|)+(description))"), + }, + } + + safeAdds = []detectionPattern{ + { + PositiveMatchRegex: regexp.MustCompile("(info.)+(.)"), + }, + { + PositiveMatchRegex: regexp.MustCompile("(tags.)+(.)"), + }, + { + PositiveMatchRegex: regexp.MustCompile("(components.)+(.|)+(schemas)+(.)"), + NegativeMatchRegex: regexp.MustCompile("(components.)+(.|)+(schemas)+(.)+(required)"), + }, + } + + safeDeletes = []detectionPattern{ + { + PositiveMatchRegex: regexp.MustCompile("(info.)+(.)"), + }, + { + PositiveMatchRegex: regexp.MustCompile("(tags.)+(.)"), + }, + } + + safeMods = []detectionPattern{ + { + PositiveMatchRegex: regexp.MustCompile("(info.)+(.)"), + }, + { + PositiveMatchRegex: regexp.MustCompile("(tags.)+(.)"), + }, + } +) + +// GetChangeDetails compares each change in a diff Proto to the relevant change type detection Patterns. +// Each change is then categorized as breaking, nonbreaking, or unknown. +func GetChangeDetails(diff *rpc.Diff) *rpc.ChangeDetails { + return &rpc.ChangeDetails{ + BreakingChanges: getBreakingChanges(diff), + NonBreakingChanges: getNonBreakingChanges(diff), + UnknownChanges: getUnknownChanges(diff), + } +} + +func getBreakingChanges(diff *rpc.Diff) *rpc.Diff { + breakingChanges := &rpc.Diff{ + Additions: []string{}, + Deletions: []string{}, + Modifications: map[string]*rpc.Diff_ValueChange{}, + } + for _, addition := range diff.GetAdditions() { + if fitsAnyPattern(unsafeAdds, addition) { + breakingChanges.Additions = append(breakingChanges.Additions, addition) + } + } + + for _, deletion := range diff.GetDeletions() { + if fitsAnyPattern(unsafeDeletes, deletion) { + breakingChanges.Deletions = append(breakingChanges.Deletions, deletion) + } + } + + for modification, modValue := range diff.GetModifications() { + if fitsAnyPattern(unsafeMods, modification) { + breakingChanges.Modifications[modification] = modValue + } + } + return breakingChanges +} + +func getNonBreakingChanges(diff *rpc.Diff) *rpc.Diff { + nonBreakingChanges := &rpc.Diff{ + Additions: []string{}, + Deletions: []string{}, + Modifications: map[string]*rpc.Diff_ValueChange{}, + } + for _, addition := range diff.GetAdditions() { + if fitsAnyPattern(safeAdds, addition) { + nonBreakingChanges.Additions = append(nonBreakingChanges.Additions, addition) + } + } + for _, deletion := range diff.GetDeletions() { + if fitsAnyPattern(safeDeletes, deletion) { + nonBreakingChanges.Deletions = append(nonBreakingChanges.Deletions, deletion) + } + } + + for modification, modValue := range diff.GetModifications() { + if fitsAnyPattern(safeMods, modification) { + nonBreakingChanges.Modifications[modification] = modValue + } + } + return nonBreakingChanges +} + +func getUnknownChanges(diff *rpc.Diff) *rpc.Diff { + unknownChanges := &rpc.Diff{ + Additions: []string{}, + Deletions: []string{}, + Modifications: map[string]*rpc.Diff_ValueChange{}, + } + for _, addition := range diff.GetAdditions() { + if !fitsAnyPattern(safeAdds, addition) && !fitsAnyPattern(unsafeAdds, addition) { + unknownChanges.Additions = append(unknownChanges.Additions, addition) + } + } + + for _, deletion := range diff.GetDeletions() { + if !fitsAnyPattern(safeDeletes, deletion) && !fitsAnyPattern(unsafeDeletes, deletion) { + unknownChanges.Deletions = append(unknownChanges.Deletions, deletion) + } + } + + for modification, modValue := range diff.GetModifications() { + if !fitsAnyPattern(safeMods, modification) && !fitsAnyPattern(unsafeMods, modification) { + unknownChanges.Modifications[modification] = modValue + } + } + return unknownChanges +} + +func fitsAnyPattern(patterns []detectionPattern, change string) bool { + for _, pattern := range patterns { + if p := pattern.NegativeMatchRegex; p != nil && p.MatchString(change) { + continue + } + if p := pattern.PositiveMatchRegex; p != nil && p.MatchString(change) { + return true + } + } + return false +} diff --git a/cmd/registry-experimental/breaking-change-detector/breaking_change_detector_test.go b/cmd/registry-experimental/breaking-change-detector/breaking_change_detector_test.go new file mode 100644 index 00000000..fac7c359 --- /dev/null +++ b/cmd/registry-experimental/breaking-change-detector/breaking_change_detector_test.go @@ -0,0 +1,165 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package breakingchangedetector + +import ( + "testing" + + "github.com/apigee/registry-experimental/rpc" + "github.com/apigee/registry/pkg/connection/grpctest" + "github.com/apigee/registry/server/registry" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "google.golang.org/protobuf/testing/protocmp" +) + +// TestMain will set up a local RegistryServer and grpc.Server for all +// tests in this package if APG_REGISTRY_ADDRESS env var is not set +// for the client. +func TestMain(m *testing.M) { + grpctest.TestMain(m, registry.Config{}) +} + +func TestChanges(t *testing.T) { + tests := []struct { + desc string + diffProto *rpc.Diff + wantProto *rpc.ChangeDetails + }{ + { + desc: "Components.Required field Addition Breaking Test", + diffProto: &rpc.Diff{ + Additions: []string{"components.schemas.x.required.x"}, + }, + wantProto: &rpc.ChangeDetails{ + BreakingChanges: &rpc.Diff{ + Additions: []string{"components.schemas.x.required.x"}, + }, + NonBreakingChanges: &rpc.Diff{}, + UnknownChanges: &rpc.Diff{}, + }, + }, + { + desc: "Components.Schemas field Deletion Breaking Test", + diffProto: &rpc.Diff{ + Deletions: []string{"components.schemas.x.x"}, + }, + wantProto: &rpc.ChangeDetails{ + BreakingChanges: &rpc.Diff{ + Deletions: []string{"components.schemas.x.x"}, + }, + NonBreakingChanges: &rpc.Diff{}, + UnknownChanges: &rpc.Diff{}, + }, + }, + { + desc: "Components.Schema.Type field Modification Breaking Test", + diffProto: &rpc.Diff{ + Modifications: map[string]*rpc.Diff_ValueChange{ + "components.schemas.x.properties.type": { + To: "float", + From: "int64", + }, + }, + }, + wantProto: &rpc.ChangeDetails{ + BreakingChanges: &rpc.Diff{ + Modifications: map[string]*rpc.Diff_ValueChange{ + "components.schemas.x.properties.type": { + To: "float", + From: "int64", + }, + }, + }, + NonBreakingChanges: &rpc.Diff{}, + UnknownChanges: &rpc.Diff{}, + }, + }, + { + desc: "Info field Addition NonBreaking Test", + diffProto: &rpc.Diff{ + Additions: []string{"info.x.x"}, + }, + wantProto: &rpc.ChangeDetails{ + BreakingChanges: &rpc.Diff{}, + NonBreakingChanges: &rpc.Diff{ + Additions: []string{"info.x.x"}, + }, + UnknownChanges: &rpc.Diff{}, + }, + }, + { + desc: "Info field Deletion NonBreaking Test", + diffProto: &rpc.Diff{ + Deletions: []string{"info.x.x"}, + }, + wantProto: &rpc.ChangeDetails{ + BreakingChanges: &rpc.Diff{}, + NonBreakingChanges: &rpc.Diff{ + Deletions: []string{"info.x.x"}, + }, + UnknownChanges: &rpc.Diff{}, + }, + }, + { + desc: "Info field Modification NonBreaking Test", + diffProto: &rpc.Diff{ + Modifications: map[string]*rpc.Diff_ValueChange{ + "info.x.x.x": { + To: "to", + From: "from", + }, + }, + }, + wantProto: &rpc.ChangeDetails{ + BreakingChanges: &rpc.Diff{}, + NonBreakingChanges: &rpc.Diff{ + Modifications: map[string]*rpc.Diff_ValueChange{ + "info.x.x.x": { + To: "to", + From: "from", + }, + }, + }, + UnknownChanges: &rpc.Diff{}, + }, + }, + { + desc: "Components.Schemas field Addition NonBreaking Test", + diffProto: &rpc.Diff{ + Additions: []string{"components.schemas.x.x"}, + }, + wantProto: &rpc.ChangeDetails{ + BreakingChanges: &rpc.Diff{}, + NonBreakingChanges: &rpc.Diff{ + Additions: []string{"components.schemas.x.x"}, + }, + UnknownChanges: &rpc.Diff{}, + }, + }, + } + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + gotProto := GetChangeDetails(test.diffProto) + opts := cmp.Options{ + protocmp.Transform(), + cmpopts.SortSlices(func(a, b string) bool { return a < b }), + } + if !cmp.Equal(test.wantProto, gotProto, opts) { + t.Errorf("GetDiff returned unexpected diff (-want +got):\n%s", cmp.Diff(test.wantProto, gotProto, opts)) + } + }) + } +} diff --git a/cmd/registry-experimental/diff/differ.go b/cmd/registry-experimental/diff/differ.go new file mode 100644 index 00000000..a66d4e64 --- /dev/null +++ b/cmd/registry-experimental/diff/differ.go @@ -0,0 +1,274 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package diff + +import ( + "fmt" + "reflect" + "strings" + + "github.com/apigee/registry-experimental/rpc" + "github.com/getkin/kin-openapi/openapi3" + "github.com/tufin/oasdiff/diff" +) + +// change represents one change in the diff. +type change struct { + fieldPath stack + changeType string +} + +type stack []string + +func (s stack) String() string { + return strings.Join(s, ".") +} + +func (s stack) isEmpty() bool { + return len(s) == 0 +} + +func (s *stack) push(str string) { + *s = append(*s, str) +} + +func (s *stack) pop() { + if s.isEmpty() { + return + } + index := len(*s) - 1 + *s = (*s)[:index] +} + +// GetDiff takes two yaml or json Open API 3 Specs and diffs them. +func GetDiff(base, revision []byte) (*rpc.Diff, error) { + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + baseSpec, err := loader.LoadFromData(base) + if err != nil { + err := fmt.Errorf("failed to load base spec from %q with %v", base, err) + return nil, err + } + revisionSpec, err := loader.LoadFromData(revision) + if err != nil { + err := fmt.Errorf("failed to load revision spec from %q with %v", revision, err) + return nil, err + } + diffReport, err := diff.Get(&diff.Config{ + ExcludeExamples: false, + ExcludeDescription: false, + }, baseSpec, revisionSpec) + if err != nil { + err := fmt.Errorf("diff failed with %v", err) + return nil, err + } + return getChanges(diffReport) +} + +func addToDiffProto(diffProto *rpc.Diff, changePath *change) { + fieldName := changePath.fieldPath.String() + switch changePath.changeType { + case "added": + diffProto.Additions = append(diffProto.Additions, fieldName) + case "deleted": + diffProto.Deletions = append(diffProto.Deletions, fieldName) + } +} + +// getChanges creates a protodiff report from a diff.Diff struct. +func getChanges(diff *diff.Diff) (*rpc.Diff, error) { + diffProto := &rpc.Diff{ + Additions: []string{}, + Deletions: []string{}, + Modifications: make(map[string]*rpc.Diff_ValueChange), + } + change := &change{ + fieldPath: stack{}, + changeType: "", + } + diffNode := reflect.ValueOf(diff) + err := searchNode(diffNode, diffProto, change) + return diffProto, err +} + +func searchNode(value reflect.Value, diffProto *rpc.Diff, changePath *change) error { + // Some values might be pointers, so we should dereference before continuing. + value = reflect.Indirect(value) + + // Invalid values aren't relevant to the diff. + if !value.IsValid() { + return nil + } + + switch value.Kind() { + case reflect.Map: + return searchMapType(value, diffProto, changePath) + case reflect.Array, reflect.Slice: + return searchArrayAndSliceType(value, diffProto, changePath) + case reflect.Struct: + return searchStructType(value, diffProto, changePath) + case reflect.Float64, reflect.String, reflect.Bool, reflect.Int: + valueString := scalarToString(value) + changePath.fieldPath.push(valueString) + addToDiffProto(diffProto, changePath) + changePath.fieldPath.pop() + return nil + default: + return fmt.Errorf("field %q has unknown type %s with value %v", changePath.fieldPath, value.Type(), value) + } +} + +func searchMapType(mapNode reflect.Value, diffProto *rpc.Diff, changePath *change) error { + if mapNode.Kind() != reflect.Map { + panic("searchMapType called with invalid type") + } + + for _, childNodeKey := range mapNode.MapKeys() { + childNode := mapNode.MapIndex(childNodeKey) + if childNode.IsZero() { + continue + } + switch childNodeKey.Interface().(type) { + case float64, string, bool, int: + childNodeKeyName := scalarToString(childNodeKey) + changePath.fieldPath.push(childNodeKeyName) + err := searchNode(childNode, diffProto, changePath) + if err != nil { + return err + } + changePath.fieldPath.pop() + continue + + case diff.Endpoint: + if endpoint, ok := childNodeKey.Interface().(diff.Endpoint); ok { + changePath.fieldPath.push(fmt.Sprintf("{%s %s}", endpoint.Method, endpoint.Path)) + err := searchNode(childNode, diffProto, changePath) + if err != nil { + return err + } + changePath.fieldPath.pop() + continue + } + return fmt.Errorf("searchMapType called with invalid diff.Endpoint type: %v", childNodeKey) + default: + return fmt.Errorf("map node key %v is not supported", childNodeKey) + } + } + return nil +} + +func searchArrayAndSliceType(arrayNode reflect.Value, diffProto *rpc.Diff, changePath *change) error { + if arrayNode.Kind() != reflect.Slice && arrayNode.Kind() != reflect.Array { + panic("searchArrayAndSliceType called with invalid type") + } + + for i := 0; i < (arrayNode.Len()); i++ { + childNode := arrayNode.Index(i) + if childNode.IsZero() { + continue + } + switch childNode.Interface().(type) { + case string: + changePath.fieldPath.push(childNode.String()) + addToDiffProto(diffProto, changePath) + changePath.fieldPath.pop() + continue + case diff.Endpoint: + if endpoint, ok := childNode.Interface().(diff.Endpoint); ok { + changePath.fieldPath.push(fmt.Sprintf("{%s %s}", endpoint.Method, endpoint.Path)) + addToDiffProto(diffProto, changePath) + changePath.fieldPath.pop() + continue + } + return fmt.Errorf("searchArrayAndSliceType called with invalid diff.Endpoint type: %v", childNode) + default: + return fmt.Errorf("array child node %v is not supported", childNode) + } + } + return nil +} + +func searchStructType(structNode reflect.Value, diffProto *rpc.Diff, changePath *change) error { + if structNode.Kind() != reflect.Struct { + panic("searchStructType called with invalid type") + } + + if vd, ok := structNode.Interface().(diff.ValueDiff); ok { + diffProto.Modifications[changePath.fieldPath.String()] = &rpc.Diff_ValueChange{ + From: scalarToString(reflect.ValueOf(vd.From)), + To: scalarToString(reflect.ValueOf(vd.To)), + } + return nil + } + for i := 0; i < structNode.NumField(); i++ { + tag, ok := structNode.Type().Field(i).Tag.Lookup("json") + if !ok { + // Fields that don't have a JSON name aren't part of the diff. + continue + } + + // Empty fields in the diff are redundant. Skip them. + childNode := structNode.Field(i) + if childNode.IsZero() { + continue + } + + // Struct field tags have the format "fieldname,omitempty". We only want the field name. + fieldName := strings.Split(tag, ",")[0] + + err := handleStructField(childNode, fieldName, diffProto, changePath) + if err != nil { + return err + } + } + return nil +} + +func handleStructField(value reflect.Value, name string, diffProto *rpc.Diff, changePath *change) error { + // Empty fields in the diff are redundant. Skip them. + if value.IsZero() { + return nil + } + + switch name { + case "added", "deleted", "modified": + changePath.changeType = name + default: + changePath.fieldPath.push(name) + defer changePath.fieldPath.pop() + } + + err := searchNode(value, diffProto, changePath) + if err != nil { + return err + } + + return nil +} + +func scalarToString(node reflect.Value) string { + switch node.Kind() { + case reflect.Float64: + return fmt.Sprintf("%f", node.Float()) + case reflect.String: + return node.String() + case reflect.Bool: + return fmt.Sprintf("%t", node.Bool()) + case reflect.Int: + return fmt.Sprintf("%d", node.Int()) + default: + return "" + } +} diff --git a/cmd/registry-experimental/diff/differ_test.go b/cmd/registry-experimental/diff/differ_test.go new file mode 100644 index 00000000..120de166 --- /dev/null +++ b/cmd/registry-experimental/diff/differ_test.go @@ -0,0 +1,310 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package diff + +import ( + "os" + "reflect" + "testing" + + "github.com/apigee/registry-experimental/rpc" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/tufin/oasdiff/diff" + "google.golang.org/protobuf/testing/protocmp" +) + +type testStruct struct { + Name string `json:"name,omitempty"` + TestMap map[string]string `json:"testmap,omitempty"` +} + +func TestDiffProtoStruct(t *testing.T) { + tests := []struct { + desc string + baseSpec string + revisionSpec string + wantProto *rpc.Diff + }{ + { + desc: "Struct Diff Added", + baseSpec: "./test-specs/base-test.yaml", + revisionSpec: "./test-specs/struct-test-add.yaml", + wantProto: &rpc.Diff{ + Additions: []string{ + "components.schemas.Pet.required.age", + "components.schemas.Pet.properties.age", + }, + Deletions: []string{}, + Modifications: map[string]*rpc.Diff_ValueChange{}, + }, + }, + { + desc: "Struct Diff Deleted", + baseSpec: "./test-specs/base-test.yaml", + revisionSpec: "./test-specs/struct-test-delete.yaml", + wantProto: &rpc.Diff{ + Additions: []string{}, + Deletions: []string{ + "components.schemas.Pet.required.name", + }, + Modifications: map[string]*rpc.Diff_ValueChange{}, + }, + }, + { + desc: "Struct Diff Reordered", + baseSpec: "./test-specs/base-test.yaml", + revisionSpec: "./test-specs/struct-test-order.yaml", + wantProto: &rpc.Diff{ + Additions: []string{}, + Deletions: []string{}, + Modifications: map[string]*rpc.Diff_ValueChange{ + "info.version": { + To: "1.0.1", + From: "1.0.0", + }, + }, + }, + }, + { + desc: "Struct Diff Modified", + baseSpec: "./test-specs/base-test.yaml", + revisionSpec: "./test-specs/struct-test-modify.yaml", + wantProto: &rpc.Diff{ + Additions: []string{}, + Deletions: []string{}, + Modifications: map[string]*rpc.Diff_ValueChange{ + "info.version": { + To: "1.0.1", + From: "1.0.0", + }, + "components.schemas.Pet.properties.tag.type": { + To: "integer", + From: "string", + }, + "components.schemas.Pet.properties.tag.format": { + To: "int64", + }, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + baseYaml, err := os.ReadFile(test.baseSpec) + if err != nil { + t.Fatalf("Failed to get base spec yaml: %s", err) + } + revisionYaml, err := os.ReadFile(test.revisionSpec) + if err != nil { + t.Fatalf("Failed to get revision spec yaml: %s", err) + } + diffProto, err := GetDiff(baseYaml, revisionYaml) + if err != nil { + t.Fatalf("Failed to get diff.Diff: %s", err) + } + opts := cmp.Options{ + protocmp.Transform(), + cmpopts.SortSlices(func(a, b string) bool { return a < b }), + } + if !cmp.Equal(test.wantProto, diffProto, opts) { + t.Errorf("GetDiff returned unexpected diff (-want +got):\n%s", cmp.Diff(test.wantProto, diffProto, opts)) + } + }) + } +} + +func TestMaps(t *testing.T) { + tests := []struct { + desc string + testMap reflect.Value + change change + wantProto *rpc.Diff + }{ + { + desc: "Map Diff String", + testMap: reflect.ValueOf(map[string]string{ + "input1": "result1", + "input2": "result2", + }), + change: change{ + fieldPath: []string{}, + changeType: "added", + }, + wantProto: &rpc.Diff{ + Additions: []string{ + "input1.result1", + "input2.result2", + }, + }, + }, { + desc: "Map Diff Endpoint Key", + testMap: reflect.ValueOf(map[diff.Endpoint]testStruct{ + { + Method: "Test-Method", + Path: "Test/Path", + }: { + Name: "TestStructResult1", + }, + }), + change: change{ + fieldPath: []string{}, + changeType: "added", + }, + wantProto: &rpc.Diff{ + Additions: []string{ + "{Test-Method Test/Path}.name.TestStructResult1", + }, + }, + }, + } + + opts := cmp.Options{protocmp.Transform(), cmpopts.SortSlices(func(a, b string) bool { return a < b })} + for _, test := range tests { + val := test.testMap + diffProto := &rpc.Diff{ + Additions: []string{}, + Deletions: []string{}, + Modifications: make(map[string]*rpc.Diff_ValueChange), + } + change := test.change + err := searchMapType(val, diffProto, &change) + if err != nil { + t.Fatalf("Failed to get diff proto, returned with error: %+v", err) + } + if !cmp.Equal(test.wantProto, diffProto, opts) { + t.Errorf("searchMapType function returned unexpected diff (-want +got):\n%s", cmp.Diff(test.wantProto, diffProto, opts)) + } + } +} + +func TestArrays(t *testing.T) { + tests := []struct { + desc string + testArray reflect.Value + change change + wantProto *rpc.Diff + }{ + { + desc: "Array Diff String", + testArray: reflect.ValueOf([]string{ + "input1", + "input2", + "input3", + "input4", + "", + }), + change: change{ + fieldPath: []string{}, + changeType: "added", + }, + wantProto: &rpc.Diff{ + Additions: []string{ + "input1", + "input2", + "input3", + "input4", + }, + }, + }, { + desc: "Array Diff Endpoint", + testArray: reflect.ValueOf([]diff.Endpoint{ + { + Method: "Test-Method-1", + Path: "Test/Path/1", + }, { + Method: "Test-Method-2", + Path: "Test/Path/2", + }, { + Method: "Test-Method-3", + Path: "Test/Path/3", + }, + }), + change: change{ + fieldPath: []string{}, + changeType: "added", + }, + wantProto: &rpc.Diff{ + Additions: []string{ + "{Test-Method-1 Test/Path/1}", + "{Test-Method-2 Test/Path/2}", + "{Test-Method-3 Test/Path/3}", + }, + }, + }, + } + + opts := cmp.Options{protocmp.Transform(), cmpopts.SortSlices(func(a, b string) bool { return a < b })} + for _, test := range tests { + val := test.testArray + diffProto := &rpc.Diff{ + Additions: []string{}, + Deletions: []string{}, + Modifications: make(map[string]*rpc.Diff_ValueChange), + } + change := test.change + err := searchArrayAndSliceType(val, diffProto, &change) + if err != nil { + t.Fatalf("Failed to get diff proto: %+v", err) + } + if !cmp.Equal(test.wantProto, diffProto, opts) { + t.Errorf("searchArrayAndSliceType returned unexpected diff (-want +got):\n%s", cmp.Diff(test.wantProto, diffProto, opts)) + } + } +} + +func TestValueDiff(t *testing.T) { + tests := []struct { + desc string + testValueDiff reflect.Value + change change + wantProto *rpc.Diff + }{ + { + desc: "Value Diff Test", + testValueDiff: reflect.ValueOf(diff.ValueDiff{ + From: 66, + To: true, + }), + change: change{ + fieldPath: []string{"ValueDiffTest"}, + changeType: "Modified", + }, + wantProto: &rpc.Diff{ + Modifications: map[string]*rpc.Diff_ValueChange{ + "ValueDiffTest": { + To: "true", + From: "66", + }, + }, + }, + }, + } + + opts := cmp.Options{protocmp.Transform(), cmpopts.SortSlices(func(a, b string) bool { return a < b })} + for _, test := range tests { + val := test.testValueDiff + diffProto := &rpc.Diff{ + Additions: []string{}, + Deletions: []string{}, + Modifications: make(map[string]*rpc.Diff_ValueChange), + } + change := test.change + _ = searchNode(val, diffProto, &change) + if !cmp.Equal(test.wantProto, diffProto, opts) { + t.Errorf("searchNode returned unexpected diff (-want +got):\n%s", cmp.Diff(test.wantProto, diffProto, opts)) + } + } +} diff --git a/cmd/registry-experimental/diff/test-specs/base-test.yaml b/cmd/registry-experimental/diff/test-specs/base-test.yaml new file mode 100644 index 00000000..c83a86c9 --- /dev/null +++ b/cmd/registry-experimental/diff/test-specs/base-test.yaml @@ -0,0 +1,33 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +openapi: "3.0.0" +info: + version: 1.0.0 + title: Test Proto +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string diff --git a/cmd/registry-experimental/diff/test-specs/struct-test-add.yaml b/cmd/registry-experimental/diff/test-specs/struct-test-add.yaml new file mode 100644 index 00000000..73d2509f --- /dev/null +++ b/cmd/registry-experimental/diff/test-specs/struct-test-add.yaml @@ -0,0 +1,36 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +openapi: "3.0.0" +info: + version: 1.0.0 + title: Test Proto +components: + schemas: + Pet: + type: object + required: + - id + - age + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + age: + type: int diff --git a/cmd/registry-experimental/diff/test-specs/struct-test-delete.yaml b/cmd/registry-experimental/diff/test-specs/struct-test-delete.yaml new file mode 100644 index 00000000..086c0fed --- /dev/null +++ b/cmd/registry-experimental/diff/test-specs/struct-test-delete.yaml @@ -0,0 +1,33 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +openapi: "3.0.0" +info: + version: 1.0.0 + title: Test Proto +components: + schemas: + Pet: + type: object + required: + - id + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + diff --git a/cmd/registry-experimental/diff/test-specs/struct-test-modify.yaml b/cmd/registry-experimental/diff/test-specs/struct-test-modify.yaml new file mode 100644 index 00000000..95af9b17 --- /dev/null +++ b/cmd/registry-experimental/diff/test-specs/struct-test-modify.yaml @@ -0,0 +1,35 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +openapi: "3.0.0" +info: + version: 1.0.1 + title: Test Proto +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: integer + format: int64 + diff --git a/cmd/registry-experimental/diff/test-specs/struct-test-order.yaml b/cmd/registry-experimental/diff/test-specs/struct-test-order.yaml new file mode 100644 index 00000000..490be922 --- /dev/null +++ b/cmd/registry-experimental/diff/test-specs/struct-test-order.yaml @@ -0,0 +1,33 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +openapi: "3.0.0" +info: + version: 1.0.1 + title: Test Proto +components: + schemas: + Pet: + type: object + required: + - name + - id + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string diff --git a/cmd/registry-experimental/metrics/metrics.go b/cmd/registry-experimental/metrics/metrics.go new file mode 100644 index 00000000..8a82048f --- /dev/null +++ b/cmd/registry-experimental/metrics/metrics.go @@ -0,0 +1,57 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metrics + +import ( + "github.com/apigee/registry-experimental/rpc" +) + +// ComputeStats will compute the ChangeStats proto for a list of Classified Diffs. +func ComputeStats(diffs ...*rpc.ChangeDetails) *rpc.ChangeStats { + var breaking int64 = 0 + var nonbreaking int64 = 0 + var unknown int64 = 0 + for _, diff := range diffs { + breaking += int64(len(diff.BreakingChanges.Additions)) + breaking += int64(len(diff.BreakingChanges.Deletions)) + breaking += int64(len(diff.BreakingChanges.Modifications)) + + nonbreaking += int64(len(diff.NonBreakingChanges.Additions)) + nonbreaking += int64(len(diff.NonBreakingChanges.Deletions)) + nonbreaking += int64(len(diff.NonBreakingChanges.Modifications)) + + unknown += int64(len(diff.UnknownChanges.Additions)) + unknown += int64(len(diff.UnknownChanges.Deletions)) + unknown += int64(len(diff.UnknownChanges.Modifications)) + } + + return &rpc.ChangeStats{ + BreakingChangeCount: breaking, + // Default Unknown Changes to Nonbreaking. + NonbreakingChangeCount: nonbreaking + unknown, + DiffCount: int64(len(diffs)), + } +} + +// ComputeMetrics will compute the metrics proto for a list of Classified Diffs. +func ComputeMetrics(stats *rpc.ChangeStats) *rpc.ChangeMetrics { + breakingChangePercentage := (float64(stats.BreakingChangeCount) / + float64(stats.BreakingChangeCount+stats.NonbreakingChangeCount)) + breakingChangeRate := float64(stats.BreakingChangeCount) / float64(stats.DiffCount) + return &rpc.ChangeMetrics{ + BreakingChangePercentage: breakingChangePercentage, + BreakingChangeRate: breakingChangeRate, + } +} diff --git a/cmd/registry-experimental/metrics/metrics_test.go b/cmd/registry-experimental/metrics/metrics_test.go new file mode 100644 index 00000000..abef576a --- /dev/null +++ b/cmd/registry-experimental/metrics/metrics_test.go @@ -0,0 +1,206 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metrics + +import ( + "testing" + + "github.com/apigee/registry-experimental/rpc" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "google.golang.org/protobuf/testing/protocmp" +) + +func TestMetrics(t *testing.T) { + tests := []struct { + desc string + diffProtos []*rpc.ChangeDetails + wantMetrics *rpc.ChangeMetrics + wantStats *rpc.ChangeStats + }{ + { + desc: "Breaking Change Percentage And Rate Test", + diffProtos: []*rpc.ChangeDetails{ + { + BreakingChanges: &rpc.Diff{ + Deletions: []string{"breakingChange"}, + Additions: []string{"breakingChange"}, + Modifications: map[string]*rpc.Diff_ValueChange{ + "breakingChange": {To: "test", From: "test"}, + }, + }, + NonBreakingChanges: &rpc.Diff{ + Deletions: []string{"Change"}, + Additions: []string{"Change"}, + Modifications: map[string]*rpc.Diff_ValueChange{ + "Change": {To: "test", From: "test"}, + }, + }, + UnknownChanges: &rpc.Diff{ + Deletions: []string{"Change"}, + Additions: []string{"Change"}, + Modifications: map[string]*rpc.Diff_ValueChange{ + "Change": {To: "test", From: "test"}, + }, + }, + }, + { + BreakingChanges: &rpc.Diff{}, + NonBreakingChanges: &rpc.Diff{ + Deletions: []string{"Change"}, + Additions: []string{"Change"}, + Modifications: map[string]*rpc.Diff_ValueChange{ + "Change": {To: "test", From: "test"}, + }, + }, + UnknownChanges: &rpc.Diff{}, + }, + }, + wantMetrics: &rpc.ChangeMetrics{ + BreakingChangePercentage: .25, + BreakingChangeRate: 1.5, + }, + wantStats: &rpc.ChangeStats{ + BreakingChangeCount: 3, + NonbreakingChangeCount: 9, + DiffCount: 2, + }, + }, + { + desc: "NonBreaking Changes Test", + diffProtos: []*rpc.ChangeDetails{ + { + BreakingChanges: &rpc.Diff{}, + NonBreakingChanges: &rpc.Diff{ + Deletions: []string{"Change"}, + Additions: []string{"Change"}, + Modifications: map[string]*rpc.Diff_ValueChange{ + "Change": {To: "test", From: "test"}, + }, + }, + UnknownChanges: &rpc.Diff{}, + }, + { + BreakingChanges: &rpc.Diff{}, + NonBreakingChanges: &rpc.Diff{ + Deletions: []string{"Change"}, + Additions: []string{"Change"}, + Modifications: map[string]*rpc.Diff_ValueChange{ + "Change": {To: "test", From: "test"}, + }, + }, + UnknownChanges: &rpc.Diff{}, + }, + }, + wantMetrics: &rpc.ChangeMetrics{ + BreakingChangePercentage: 0, + BreakingChangeRate: 0, + }, + wantStats: &rpc.ChangeStats{ + BreakingChangeCount: 0, + NonbreakingChangeCount: 6, + DiffCount: 2, + }, + }, + { + desc: "Unknown Default to NonBreaking Changes Test", + diffProtos: []*rpc.ChangeDetails{ + { + BreakingChanges: &rpc.Diff{}, + NonBreakingChanges: &rpc.Diff{}, + UnknownChanges: &rpc.Diff{ + Deletions: []string{"Change"}, + Additions: []string{"Change"}, + Modifications: map[string]*rpc.Diff_ValueChange{ + "Change": {To: "test", From: "test"}, + }, + }, + }, + { + BreakingChanges: &rpc.Diff{}, + NonBreakingChanges: &rpc.Diff{}, + UnknownChanges: &rpc.Diff{ + Deletions: []string{"Change"}, + Additions: []string{"Change"}, + Modifications: map[string]*rpc.Diff_ValueChange{ + "Change": {To: "test", From: "test"}, + }, + }, + }, + }, + wantMetrics: &rpc.ChangeMetrics{ + BreakingChangePercentage: 0, + BreakingChangeRate: 0, + }, + wantStats: &rpc.ChangeStats{ + BreakingChangeCount: 0, + NonbreakingChangeCount: 6, + DiffCount: 2, + }, + }, + { + desc: "Breaking Changes Test", + diffProtos: []*rpc.ChangeDetails{ + { + BreakingChanges: &rpc.Diff{ + Deletions: []string{"breakingChange"}, + Additions: []string{"breakingChange"}, + Modifications: map[string]*rpc.Diff_ValueChange{ + "breakingChange": {To: "test", From: "test"}, + }, + }, + NonBreakingChanges: &rpc.Diff{}, + UnknownChanges: &rpc.Diff{}, + }, + { + BreakingChanges: &rpc.Diff{ + Deletions: []string{"breakingChange"}, + Additions: []string{"breakingChange"}, + Modifications: map[string]*rpc.Diff_ValueChange{ + "breakingChange": {To: "test", From: "test"}, + }, + }, + NonBreakingChanges: &rpc.Diff{}, + UnknownChanges: &rpc.Diff{}, + }, + }, + wantMetrics: &rpc.ChangeMetrics{ + BreakingChangePercentage: 1, + BreakingChangeRate: 3, + }, + wantStats: &rpc.ChangeStats{ + BreakingChangeCount: 6, + NonbreakingChangeCount: 0, + DiffCount: 2, + }, + }, + } + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + gotStats := ComputeStats(test.diffProtos...) + gotMetrics := ComputeMetrics(gotStats) + opts := cmp.Options{ + protocmp.Transform(), + cmpopts.SortSlices(func(a, b string) bool { return a < b }), + } + if !cmp.Equal(test.wantMetrics, gotMetrics, opts) { + t.Errorf("ComputeMetrics returned unexpected diff (-want +got):\n%s", cmp.Diff(test.wantMetrics, gotMetrics, opts)) + } + if !cmp.Equal(test.wantStats, gotStats, opts) { + t.Errorf("ComputeStats returned unexpected diff (-want +got):\n%s", cmp.Diff(test.wantStats, gotStats, opts)) + } + }) + } +} diff --git a/go.mod b/go.mod index 374a860e..71568be0 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/blevesearch/bleve v1.0.14 github.com/envoyproxy/go-control-plane v0.10.3 github.com/fatih/camelcase v1.0.0 + github.com/getkin/kin-openapi v0.103.0 github.com/gogo/googleapis v1.4.1 github.com/golang/protobuf v1.5.2 github.com/google/gnostic v0.6.9 @@ -25,7 +26,8 @@ require ( github.com/spf13/cobra v1.5.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.13.0 - golang.org/x/exp v0.0.0-20220915210609-840b3808d824 + github.com/tufin/oasdiff v1.0.9 + golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 golang.org/x/net v0.6.0 golang.org/x/oauth2 v0.5.0 google.golang.org/api v0.110.0 @@ -39,8 +41,13 @@ require ( ) require ( - cloud.google.com/go v0.107.0 // indirect + cloud.google.com/go v0.110.0 // indirect cloud.google.com/go/compute v1.18.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/invopop/yaml v0.2.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect ) require ( diff --git a/go.sum b/go.sum index e8430cb4..08737418 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,8 @@ cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2Z cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -168,6 +168,7 @@ github.com/couchbase/vellum v1.0.2/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLB github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= @@ -204,6 +205,9 @@ github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/getkin/kin-openapi v0.87.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/getkin/kin-openapi v0.103.0 h1:F5wAtaQvPWxKCAYZ69LgHAThgu16p4u41VQtbn1U8LA= +github.com/getkin/kin-openapi v0.103.0/go.mod h1:w4lRPHiyOdwGbOkLIyk+P0qCwlu7TXPCHD/64nSXzgE= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= @@ -215,6 +219,12 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -328,6 +338,7 @@ github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+ github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/graphql-go/graphql v0.8.0 h1:JHRQMeQjofwqVvGwYnr8JnPTY0AxgVy1HpHSGPLdH0I= github.com/graphql-go/graphql v0.8.0/go.mod h1:nKiHzRM0qopJEwCITUuIsxk9PlVlwIiiI8pnJEhordQ= github.com/graphql-go/handler v0.2.3 h1:CANh8WPnl5M9uA25c2GBhPqJhE53Fg0Iue/fRNla71E= @@ -348,6 +359,9 @@ github.com/ikawaha/kagome.ipadic v1.1.2/go.mod h1:DPSBbU0czaJhAb/5uKQZHMc9MTVRpD github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= +github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -405,6 +419,8 @@ github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= @@ -420,11 +436,13 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -436,6 +454,10 @@ github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuz github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -556,6 +578,8 @@ github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= +github.com/tufin/oasdiff v1.0.9 h1:eoUIJ0xwRAUHQLJXO+CCskHNFkK2pS1HTuui/RQByt8= +github.com/tufin/oasdiff v1.0.9/go.mod h1:OASLqeA27MdLElf5p5L90Vl6O66ant3buWy6W1RX0Yk= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= @@ -568,6 +592,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg= github.com/yuin/goldmark v1.4.14 h1:jwww1XQfhJN7Zm+/a1ZA/3WUiEBEroYFNTiV3dKwM8U= github.com/yuin/goldmark v1.4.14/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= @@ -633,8 +658,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220915210609-840b3808d824 h1:iRn89vW39OtFmjlBnUXpaiKIPyYGeq2SwquynSjoB/k= -golang.org/x/exp v0.0.0-20220915210609-840b3808d824/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1115,6 +1140,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= @@ -1126,12 +1152,14 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.3.10 h1:Fsd+pQpFMGlGxxVMUPJhNo8gG8B1lKtk8QQ4/VZZAJw= diff --git a/google/cloud/apigeeregistry/v1/analysis/diff_analytics.proto b/google/cloud/apigeeregistry/v1/analysis/diff_analytics.proto new file mode 100644 index 00000000..ce0c75a5 --- /dev/null +++ b/google/cloud/apigeeregistry/v1/analysis/diff_analytics.proto @@ -0,0 +1,82 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; +package google.cloud.apigeeregistry.v1.analysis; +// [START go_declaration] +option go_package = "github.com/apigee/registry-experimental/rpc;rpc"; +// [END go_declaration] + +/*Diff contains the diff of a spec and its revision. */ +message Diff { + /* additions holds every addition change in the diff. + The string will hold the entire field path of one addition change in the + format foo.bar.x .*/ + repeated string additions = 1; + /* deletions holds every deletion change in the diff. + The string will hold the entire field path of one deletion change in the + format foo.bar.x .*/ + repeated string deletions = 2; + + // ValueChange hold the values of the elements that changed in one diff change. + message ValueChange { + // from represents the previous value of the element. + string from = 1; + // to represents the current value of the element. + string to = 2; + } + /* modifications holds every modification change in the diff. + The string key will hold the field path of one modification change in the + format foo.bar.x. + The value of the key will represent the element that was modified in the + field. */ + map modifications = 3; +} + +/* ChangeDetails classifies changes from diff into separate categories. */ +message ChangeDetails { + /* breakingChanges is a Diff proto that only contains the breaking changes + of a diff.*/ + Diff breaking_changes = 1; + /* nonBreakingChanges is a Diff proto that only contains the non-breaking + changes of a diff.*/ + Diff non_breaking_changes = 2; + /* unknownChanges is a Diff proto that contains all the changes that could not + be classified in the other categories.*/ + Diff unknown_changes = 3; +} + +/* ChangeStats holds information relating to a list of diffs*/ +message ChangeStats { + // breaking_change_count represents the total number of breaking changes. + int64 breaking_change_count = 1; + // nonbreaking_change_count represents the total number of non-breaking changes. + int64 nonbreaking_change_count = 2; + // diff_count represents the number of diffs used in this stats + int64 diff_count = 3; + } + +/* ChangeMetrics holds metrics about a list of diffs. Each metric is computed from +two or more stats. */ +message ChangeMetrics { + /* breaking_change_percentage is the percentage of changes that are breaking. + It is computed by the equation + (breaking_change_count / (nonbreaking_change_count + breaking_change_count))*/ + double breaking_change_percentage = 1; + /* breaking_change_rate is the average number of breaking changes that are + introduced per Diff. + It is computed by the equation + ((nonbreaking_change_count + breaking_change_count) / diff_count)*/ + double breaking_change_rate = 2; + } diff --git a/rpc/diff_analytics.pb.go b/rpc/diff_analytics.pb.go new file mode 100644 index 00000000..0b2bf970 --- /dev/null +++ b/rpc/diff_analytics.pb.go @@ -0,0 +1,564 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.30.0 +// protoc v3.21.9 +// source: google/cloud/apigeeregistry/v1/analysis/diff_analytics.proto + +package rpc + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Diff contains the diff of a spec and its revision. +type Diff struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // additions holds every addition change in the diff. + // The string will hold the entire field path of one addition change in the + // format foo.bar.x . + Additions []string `protobuf:"bytes,1,rep,name=additions,proto3" json:"additions,omitempty"` + // deletions holds every deletion change in the diff. + // The string will hold the entire field path of one deletion change in the + // format foo.bar.x . + Deletions []string `protobuf:"bytes,2,rep,name=deletions,proto3" json:"deletions,omitempty"` + // modifications holds every modification change in the diff. + // The string key will hold the field path of one modification change in the + // format foo.bar.x. + // The value of the key will represent the element that was modified in the + // field. + Modifications map[string]*Diff_ValueChange `protobuf:"bytes,3,rep,name=modifications,proto3" json:"modifications,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *Diff) Reset() { + *x = Diff{} + if protoimpl.UnsafeEnabled { + mi := &file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Diff) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Diff) ProtoMessage() {} + +func (x *Diff) ProtoReflect() protoreflect.Message { + mi := &file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Diff.ProtoReflect.Descriptor instead. +func (*Diff) Descriptor() ([]byte, []int) { + return file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDescGZIP(), []int{0} +} + +func (x *Diff) GetAdditions() []string { + if x != nil { + return x.Additions + } + return nil +} + +func (x *Diff) GetDeletions() []string { + if x != nil { + return x.Deletions + } + return nil +} + +func (x *Diff) GetModifications() map[string]*Diff_ValueChange { + if x != nil { + return x.Modifications + } + return nil +} + +// ChangeDetails classifies changes from diff into separate categories. +type ChangeDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // breakingChanges is a Diff proto that only contains the breaking changes + // of a diff. + BreakingChanges *Diff `protobuf:"bytes,1,opt,name=breaking_changes,json=breakingChanges,proto3" json:"breaking_changes,omitempty"` + // nonBreakingChanges is a Diff proto that only contains the non-breaking + // changes of a diff. + NonBreakingChanges *Diff `protobuf:"bytes,2,opt,name=non_breaking_changes,json=nonBreakingChanges,proto3" json:"non_breaking_changes,omitempty"` + // unknownChanges is a Diff proto that contains all the changes that could not + // be classified in the other categories. + UnknownChanges *Diff `protobuf:"bytes,3,opt,name=unknown_changes,json=unknownChanges,proto3" json:"unknown_changes,omitempty"` +} + +func (x *ChangeDetails) Reset() { + *x = ChangeDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChangeDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChangeDetails) ProtoMessage() {} + +func (x *ChangeDetails) ProtoReflect() protoreflect.Message { + mi := &file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChangeDetails.ProtoReflect.Descriptor instead. +func (*ChangeDetails) Descriptor() ([]byte, []int) { + return file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDescGZIP(), []int{1} +} + +func (x *ChangeDetails) GetBreakingChanges() *Diff { + if x != nil { + return x.BreakingChanges + } + return nil +} + +func (x *ChangeDetails) GetNonBreakingChanges() *Diff { + if x != nil { + return x.NonBreakingChanges + } + return nil +} + +func (x *ChangeDetails) GetUnknownChanges() *Diff { + if x != nil { + return x.UnknownChanges + } + return nil +} + +// ChangeStats holds information relating to a list of diffs +type ChangeStats struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // breaking_change_count represents the total number of breaking changes. + BreakingChangeCount int64 `protobuf:"varint,1,opt,name=breaking_change_count,json=breakingChangeCount,proto3" json:"breaking_change_count,omitempty"` + // nonbreaking_change_count represents the total number of non-breaking changes. + NonbreakingChangeCount int64 `protobuf:"varint,2,opt,name=nonbreaking_change_count,json=nonbreakingChangeCount,proto3" json:"nonbreaking_change_count,omitempty"` + // diff_count represents the number of diffs used in this stats + DiffCount int64 `protobuf:"varint,3,opt,name=diff_count,json=diffCount,proto3" json:"diff_count,omitempty"` +} + +func (x *ChangeStats) Reset() { + *x = ChangeStats{} + if protoimpl.UnsafeEnabled { + mi := &file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChangeStats) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChangeStats) ProtoMessage() {} + +func (x *ChangeStats) ProtoReflect() protoreflect.Message { + mi := &file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChangeStats.ProtoReflect.Descriptor instead. +func (*ChangeStats) Descriptor() ([]byte, []int) { + return file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDescGZIP(), []int{2} +} + +func (x *ChangeStats) GetBreakingChangeCount() int64 { + if x != nil { + return x.BreakingChangeCount + } + return 0 +} + +func (x *ChangeStats) GetNonbreakingChangeCount() int64 { + if x != nil { + return x.NonbreakingChangeCount + } + return 0 +} + +func (x *ChangeStats) GetDiffCount() int64 { + if x != nil { + return x.DiffCount + } + return 0 +} + +// ChangeMetrics holds metrics about a list of diffs. Each metric is computed from +// two or more stats. +type ChangeMetrics struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // breaking_change_percentage is the percentage of changes that are breaking. + // It is computed by the equation + // (breaking_change_count / (nonbreaking_change_count + breaking_change_count)) + BreakingChangePercentage float64 `protobuf:"fixed64,1,opt,name=breaking_change_percentage,json=breakingChangePercentage,proto3" json:"breaking_change_percentage,omitempty"` + // breaking_change_rate is the average number of breaking changes that are + // introduced per Diff. + // It is computed by the equation + // ((nonbreaking_change_count + breaking_change_count) / diff_count) + BreakingChangeRate float64 `protobuf:"fixed64,2,opt,name=breaking_change_rate,json=breakingChangeRate,proto3" json:"breaking_change_rate,omitempty"` +} + +func (x *ChangeMetrics) Reset() { + *x = ChangeMetrics{} + if protoimpl.UnsafeEnabled { + mi := &file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChangeMetrics) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChangeMetrics) ProtoMessage() {} + +func (x *ChangeMetrics) ProtoReflect() protoreflect.Message { + mi := &file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChangeMetrics.ProtoReflect.Descriptor instead. +func (*ChangeMetrics) Descriptor() ([]byte, []int) { + return file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDescGZIP(), []int{3} +} + +func (x *ChangeMetrics) GetBreakingChangePercentage() float64 { + if x != nil { + return x.BreakingChangePercentage + } + return 0 +} + +func (x *ChangeMetrics) GetBreakingChangeRate() float64 { + if x != nil { + return x.BreakingChangeRate + } + return 0 +} + +// ValueChange hold the values of the elements that changed in one diff change. +type Diff_ValueChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // from represents the previous value of the element. + From string `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + // to represents the current value of the element. + To string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` +} + +func (x *Diff_ValueChange) Reset() { + *x = Diff_ValueChange{} + if protoimpl.UnsafeEnabled { + mi := &file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Diff_ValueChange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Diff_ValueChange) ProtoMessage() {} + +func (x *Diff_ValueChange) ProtoReflect() protoreflect.Message { + mi := &file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Diff_ValueChange.ProtoReflect.Descriptor instead. +func (*Diff_ValueChange) Descriptor() ([]byte, []int) { + return file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDescGZIP(), []int{0, 0} +} + +func (x *Diff_ValueChange) GetFrom() string { + if x != nil { + return x.From + } + return "" +} + +func (x *Diff_ValueChange) GetTo() string { + if x != nil { + return x.To + } + return "" +} + +var File_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto protoreflect.FileDescriptor + +var file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDesc = []byte{ + 0x0a, 0x3c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x61, + 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x76, 0x31, + 0x2f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x2f, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x61, + 0x6e, 0x61, 0x6c, 0x79, 0x74, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x27, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, + 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x61, + 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x22, 0xda, 0x02, 0x0a, 0x04, 0x44, 0x69, 0x66, 0x66, + 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1c, + 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x66, 0x0a, 0x0d, + 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, + 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x2e, 0x44, 0x69, + 0x66, 0x66, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x31, 0x0a, 0x0b, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x1a, 0x7b, 0x0a, 0x12, 0x4d, 0x6f, 0x64, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x4f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, + 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, + 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x2e, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa2, 0x02, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x58, 0x0a, 0x10, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x69, + 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, + 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, + 0x31, 0x2e, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x52, + 0x0f, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x12, 0x5f, 0x0a, 0x14, 0x6e, 0x6f, 0x6e, 0x5f, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x69, 0x6e, 0x67, + 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, + 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, + 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x52, 0x12, 0x6e, + 0x6f, 0x6e, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x73, 0x12, 0x56, 0x0a, 0x0f, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, + 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x61, 0x6e, 0x61, 0x6c, + 0x79, 0x73, 0x69, 0x73, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x52, 0x0e, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, + 0x77, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x0b, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x69, + 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x38, 0x0a, + 0x18, 0x6e, 0x6f, 0x6e, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x16, 0x6e, 0x6f, 0x6e, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x5f, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x64, 0x69, 0x66, + 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x7f, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x3c, 0x0a, 0x1a, 0x62, 0x72, 0x65, 0x61, 0x6b, + 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, + 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x18, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x65, 0x72, 0x63, 0x65, + 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x69, 0x6e, + 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x12, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x52, 0x61, 0x74, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x2f, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x79, 0x2d, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, + 0x61, 0x6c, 0x2f, 0x72, 0x70, 0x63, 0x3b, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDescOnce sync.Once + file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDescData = file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDesc +) + +func file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDescGZIP() []byte { + file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDescOnce.Do(func() { + file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDescData) + }) + return file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDescData +} + +var file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_goTypes = []interface{}{ + (*Diff)(nil), // 0: google.cloud.apigeeregistry.v1.analysis.Diff + (*ChangeDetails)(nil), // 1: google.cloud.apigeeregistry.v1.analysis.ChangeDetails + (*ChangeStats)(nil), // 2: google.cloud.apigeeregistry.v1.analysis.ChangeStats + (*ChangeMetrics)(nil), // 3: google.cloud.apigeeregistry.v1.analysis.ChangeMetrics + (*Diff_ValueChange)(nil), // 4: google.cloud.apigeeregistry.v1.analysis.Diff.ValueChange + nil, // 5: google.cloud.apigeeregistry.v1.analysis.Diff.ModificationsEntry +} +var file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_depIdxs = []int32{ + 5, // 0: google.cloud.apigeeregistry.v1.analysis.Diff.modifications:type_name -> google.cloud.apigeeregistry.v1.analysis.Diff.ModificationsEntry + 0, // 1: google.cloud.apigeeregistry.v1.analysis.ChangeDetails.breaking_changes:type_name -> google.cloud.apigeeregistry.v1.analysis.Diff + 0, // 2: google.cloud.apigeeregistry.v1.analysis.ChangeDetails.non_breaking_changes:type_name -> google.cloud.apigeeregistry.v1.analysis.Diff + 0, // 3: google.cloud.apigeeregistry.v1.analysis.ChangeDetails.unknown_changes:type_name -> google.cloud.apigeeregistry.v1.analysis.Diff + 4, // 4: google.cloud.apigeeregistry.v1.analysis.Diff.ModificationsEntry.value:type_name -> google.cloud.apigeeregistry.v1.analysis.Diff.ValueChange + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_init() } +func file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_init() { + if File_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Diff); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChangeDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChangeStats); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChangeMetrics); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Diff_ValueChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_goTypes, + DependencyIndexes: file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_depIdxs, + MessageInfos: file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_msgTypes, + }.Build() + File_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto = out.File + file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_rawDesc = nil + file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_goTypes = nil + file_google_cloud_apigeeregistry_v1_analysis_diff_analytics_proto_depIdxs = nil +} diff --git a/tools/PROTOS.sh b/tools/PROTOS.sh index e7b27a8b..784b1814 100644 --- a/tools/PROTOS.sh +++ b/tools/PROTOS.sh @@ -17,6 +17,7 @@ ALL_PROTOS=( google/cloud/apigeeregistry/v1/*.proto + google/cloud/apigeeregistry/v1/analysis/*.proto ) SERVICE_PROTOS=(