From 65fa21be86150128e17df7dfce1fd9d7ed354ac9 Mon Sep 17 00:00:00 2001 From: Dave Shanley Date: Mon, 3 Oct 2022 09:46:12 -0400 Subject: [PATCH] Continued pattern building, added info changes tried to make some more generic patterns, but I think it's about as thin as it can be now, some boilerplate will be required. Signed-off-by: Dave Shanley --- datamodel/low/v3/constants.go | 5 + what-changed/comparison_functions.go | 2 +- what-changed/info.go | 116 ++++++++ what-changed/info_test.go | 393 +++++++++++++++++++++++++++ what-changed/models.go | 4 +- what-changed/what_changed_test.go | 1 - 6 files changed, 517 insertions(+), 4 deletions(-) create mode 100644 what-changed/info.go create mode 100644 what-changed/info_test.go diff --git a/datamodel/low/v3/constants.go b/datamodel/low/v3/constants.go index 90d0bf6..ee7db64 100644 --- a/datamodel/low/v3/constants.go +++ b/datamodel/low/v3/constants.go @@ -42,4 +42,9 @@ const ( URLLabel = "url" NameLabel = "name" EmailLabel = "email" + TitleLabel = "title" + TermsOfServiceLabel = "termsOfService" + VersionLabel = "version" + LicenseLabel = "license" + ContactLabel = "contact" ) diff --git a/what-changed/comparison_functions.go b/what-changed/comparison_functions.go index 1078753..113464b 100644 --- a/what-changed/comparison_functions.go +++ b/what-changed/comparison_functions.go @@ -11,7 +11,7 @@ import ( // CreateChange is a generic function that will create a Change of type T, populate all properties if set, and then // add a pointer to Change[T] in the slice of Change pointers provided func CreateChange[T any](changes *[]*Change[T], changeType int, property string, leftValueNode, rightValueNode *yaml.Node, - breaking bool, originalObject, newObject T) *[]*Change[T] { + breaking bool, originalObject, newObject any) *[]*Change[T] { // create a new context for the left and right nodes. ctx := CreateContext(leftValueNode, rightValueNode) diff --git a/what-changed/info.go b/what-changed/info.go new file mode 100644 index 0000000..355ed7f --- /dev/null +++ b/what-changed/info.go @@ -0,0 +1,116 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package what_changed + +import ( + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/datamodel/low/v3" +) + +// InfoChanges represents the number of changes to an Info object. Part of an OpenAPI document +type InfoChanges struct { + PropertyChanges[*base.Info] + ContactChanges *ContactChanges + LicenseChanges *LicenseChanges +} + +// TotalChanges represents the total number of changes made to an Info object. +func (i *InfoChanges) TotalChanges() int { + t := len(i.Changes) + if i.ContactChanges != nil { + t += i.ContactChanges.TotalChanges() + } + if i.LicenseChanges != nil { + t += i.LicenseChanges.TotalChanges() + } + return t +} + +func CompareInfo(l, r *base.Info) *InfoChanges { + + var changes []*Change[*base.Info] + var props []*PropertyCheck[*base.Info] + + // Title + props = append(props, &PropertyCheck[*base.Info]{ + LeftNode: l.Title.ValueNode, + RightNode: r.Title.ValueNode, + Label: v3.TitleLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) + + // Description + props = append(props, &PropertyCheck[*base.Info]{ + LeftNode: l.Description.ValueNode, + RightNode: r.Description.ValueNode, + Label: v3.DescriptionLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) + + // TermsOfService + props = append(props, &PropertyCheck[*base.Info]{ + LeftNode: l.TermsOfService.ValueNode, + RightNode: r.TermsOfService.ValueNode, + Label: v3.TermsOfServiceLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) + + // Version + props = append(props, &PropertyCheck[*base.Info]{ + LeftNode: l.Version.ValueNode, + RightNode: r.Version.ValueNode, + Label: v3.VersionLabel, + Changes: &changes, + Breaking: false, + Original: l, + New: r, + }) + + // check properties + CheckProperties(props) + + i := new(InfoChanges) + + // compare contact. + if l.Contact.Value != nil && r.Contact.Value != nil { + i.ContactChanges = CompareContact(l.Contact.Value, r.Contact.Value) + } else { + if l.Contact.Value == nil && r.Contact.Value != nil { + CreateChange[*base.Info](&changes, ObjectAdded, v3.ContactLabel, + nil, r.Contact.ValueNode, false, nil, r.Contact.Value) + } + if l.Contact.Value != nil && r.Contact.Value == nil { + CreateChange[*base.Info](&changes, ObjectRemoved, v3.ContactLabel, + l.Contact.ValueNode, nil, false, l.Contact.Value, nil) + } + } + + // compare license. + if l.License.Value != nil && r.License.Value != nil { + i.LicenseChanges = CompareLicense(l.License.Value, r.License.Value) + } else { + if l.License.Value == nil && r.License.Value != nil { + CreateChange[*base.Info](&changes, ObjectAdded, v3.LicenseLabel, + nil, r.License.ValueNode, false, nil, r.License.Value) + } + if l.License.Value != nil && r.License.Value == nil { + CreateChange[*base.Info](&changes, ObjectRemoved, v3.LicenseLabel, + l.License.ValueNode, nil, false, r.License.Value, nil) + } + } + if len(changes) <= 0 && i.ContactChanges == nil && i.LicenseChanges == nil { + return nil + } + i.Changes = changes + return i +} diff --git a/what-changed/info_test.go b/what-changed/info_test.go new file mode 100644 index 0000000..a86b87c --- /dev/null +++ b/what-changed/info_test.go @@ -0,0 +1,393 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package what_changed + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" +) + +func TestCompareInfo_DescriptionAdded(t *testing.T) { + + left := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + right := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +description: this is a description +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) + + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(&lNode, &lDoc) + _ = low.BuildModel(&rNode, &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) + + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.DescriptionLabel, extChanges.Changes[0].Property) +} + +func TestCompareInfo_TitleRemoved(t *testing.T) { + + left := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +description: this is a description +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + right := `termsOfService: https://pb33f.io/terms +version: '1.2.3' +description: this is a description +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) + + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(&lNode, &lDoc) + _ = low.BuildModel(&rNode, &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) + + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.TitleLabel, extChanges.Changes[0].Property) +} + +func TestCompareInfo_VersionModified(t *testing.T) { + + left := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + right := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '99.99' +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) + + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(&lNode, &lDoc) + _ = low.BuildModel(&rNode, &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) + + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, Modified, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.VersionLabel, extChanges.Changes[0].Property) +} + +func TestCompareInfo_RemoveLicense(t *testing.T) { + + left := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + right := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +contact: + name: buckaroo + email: buckaroo@pb33f.io` + + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) + + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(&lNode, &lDoc) + _ = low.BuildModel(&rNode, &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) + + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.LicenseLabel, extChanges.Changes[0].Property) +} + +func TestCompareInfo_AddLicense(t *testing.T) { + + left := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +contact: + name: buckaroo + email: buckaroo@pb33f.io` + + right := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) + + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(&lNode, &lDoc) + _ = low.BuildModel(&rNode, &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) + + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.LicenseLabel, extChanges.Changes[0].Property) +} + +func TestCompareInfo_LicenseChanged(t *testing.T) { + + left := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + right := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: Apache` + + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) + + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(&lNode, &lDoc) + _ = low.BuildModel(&rNode, &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) + + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, Modified, extChanges.LicenseChanges.Changes[0].ChangeType) + assert.Equal(t, v3.NameLabel, extChanges.LicenseChanges.Changes[0].Property) +} + +func TestCompareInfo_AddContact(t *testing.T) { + + left := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +license: + name: MIT` + + right := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) + + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(&lNode, &lDoc) + _ = low.BuildModel(&rNode, &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) + + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.ContactLabel, extChanges.Changes[0].Property) +} + +func TestCompareInfo_RemoveContact(t *testing.T) { + + left := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + right := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +license: + name: MIT` + + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) + + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(&lNode, &lDoc) + _ = low.BuildModel(&rNode, &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) + + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType) + assert.Equal(t, v3.ContactLabel, extChanges.Changes[0].Property) +} + +func TestCompareInfo_ContactModified(t *testing.T) { + + left := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + right := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +contact: + name: the buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) + + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(&lNode, &lDoc) + _ = low.BuildModel(&rNode, &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) + + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, Modified, extChanges.ContactChanges.Changes[0].ChangeType) + assert.Equal(t, v3.NameLabel, extChanges.ContactChanges.Changes[0].Property) +} + +func TestCompareInfo_Equal(t *testing.T) { + + left := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + right := `title: a nice spec +termsOfService: https://pb33f.io/terms +version: '1.2.3' +contact: + name: buckaroo + email: buckaroo@pb33f.io +license: + name: MIT` + + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) + + // create low level objects + var lDoc base.Info + var rDoc base.Info + _ = low.BuildModel(&lNode, &lDoc) + _ = low.BuildModel(&rNode, &rDoc) + _ = lDoc.Build(lNode.Content[0], nil) + _ = rDoc.Build(rNode.Content[0], nil) + + // compare. + extChanges := CompareInfo(&lDoc, &rDoc) + assert.Nil(t, extChanges) +} diff --git a/what-changed/models.go b/what-changed/models.go index eb30e6e..216bd80 100644 --- a/what-changed/models.go +++ b/what-changed/models.go @@ -75,10 +75,10 @@ type Change[T any] struct { Breaking bool // OriginalObject represents the original object that was changed. - OriginalObject T + OriginalObject any // NewObject represents the new object that has been modified. - NewObject T + NewObject any } // PropertyChanges holds a slice of Change[T] change pointers diff --git a/what-changed/what_changed_test.go b/what-changed/what_changed_test.go index 6e78fbd..0a95109 100644 --- a/what-changed/what_changed_test.go +++ b/what-changed/what_changed_test.go @@ -2,4 +2,3 @@ // SPDX-License-Identifier: MIT package what_changed -