Skip to content

Commit

Permalink
Added what-changed docs to readme
Browse files Browse the repository at this point in the history
and some examples for go-docs

Signed-off-by: Dave Shanley <[email protected]>
  • Loading branch information
daveshanley committed Nov 21, 2022
1 parent 62d580e commit cd7f24f
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 0 deletions.
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ version of `libopenapi`, which isn't something that can be released as it's cust
`libopenapi` is the result of years of learning and battle testing OpenAPI in golang. This library represents what would
have been created, if we knew then - what we know now.

## what-changed: a complete diff engine

Compare Swagger and OpenAPI documents for all changes made across the specification. Every detail is checked across
all versions of OpenAPI and returns a simple to navigate report of every single change made.

Everything modified, added or removed is reported, complete with original/new values, line numbers and columns.

> Need to know which **line**, or **column** number a key or value for something is? **`libopenapi` has you covered**.
## Comes with an Index and a Resolver
Expand Down Expand Up @@ -198,6 +205,61 @@ fmt.Printf("value is %s, the value is on line %d, " +
```
---

## What changed?

libopenapi comes with a complete **diff engine**

Want to know what changed between two different OpenAPI or Swagger specifications? libopenapi makes this super, super easy.

```go
// How to compare two different OpenAPI specifications for changes between them.

// load an original OpenAPI 3 specification from bytes
burgerShopOriginal, _ := ioutil.ReadFile("test_specs/burgershop.openapi.yaml")

// load an **updated** OpenAPI 3 specification from bytes
burgerShopUpdated, _ := ioutil.ReadFile("test_specs/burgershop.openapi-modified.yaml")

// create a new document from original specification bytes
originalDoc, err := NewDocument(burgerShopOriginal)

// if anything went wrong, an error is thrown
if err != nil {
panic(fmt.Sprintf("cannot create new document: %e", err))
}

// create a new document from updated specification bytes
updatedDoc, err := NewDocument(burgerShopUpdated)

// if anything went wrong, an error is thrown
if err != nil {
panic(fmt.Sprintf("cannot create new document: %e", err))
}

// Compare documents for all changes made
documentChanges, errs := CompareDocuments(originalDoc, updatedDoc)

// If anything went wrong when building models for documents.
if len(errs) > 0 {
for i := range errs {
fmt.Printf("error: %e\n", errs[i])
}
panic(fmt.Sprintf("cannot compare documents: %d errors reported", len(errs)))
}

// Extract SchemaChanges from components changes.
schemaChanges := documentChanges.ComponentsChanges.SchemaChanges

// Print out some interesting stats about the OpenAPI document changes.
fmt.Printf("There are %d changes, of which %d are breaking. %v schemas have changes.",
documentChanges.TotalChanges(), documentChanges.TotalBreakingChanges(), len(schemaChanges))

```

Every change can be explored and navigated just like you would use the high or low level models.

---

## But wait, there's more - Mutating the model

Having a read-only model is great, but what about when we want to modify the model and mutate values, or even add new
Expand Down
41 changes: 41 additions & 0 deletions document.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
v2low "github.com/pb33f/libopenapi/datamodel/low/v2"
v3low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/utils"
what_changed "github.com/pb33f/libopenapi/what-changed"
"github.com/pb33f/libopenapi/what-changed/model"
"gopkg.in/yaml.v3"
)

Expand Down Expand Up @@ -144,3 +146,42 @@ func (d *document) BuildV3Model() (*DocumentModel[v3high.Document], []error) {
Model: *highDoc,
}, nil
}

// CompareDocuments will accept a left and right Document implementing struct, build a model for the correct
// version and then compare model documents for changes.
//
// If there are any errors when building the models, those errors are returned with a nil pointer for the
// model.DocumentChanges. If there are any changes found however between either Document, then a pointer to
// model.DocumentChanges is returned containing every single change, broken down, model by model.
func CompareDocuments(original, updated Document) (*model.DocumentChanges, []error) {
var errors []error
if original.GetSpecInfo().SpecType == utils.OpenApi3 && updated.GetSpecInfo().SpecType == utils.OpenApi3 {
v3ModelLeft, errs := original.BuildV3Model()
if len(errs) > 0 {
errors = errs
}
v3ModelRight, errs := updated.BuildV3Model()
if len(errs) > 0 {
errors = errs
}
if len(errors) > 0 {
return nil, errors
}
return what_changed.CompareOpenAPIDocuments(v3ModelLeft.Model.GoLow(), v3ModelRight.Model.GoLow()), nil
}
if original.GetSpecInfo().SpecType == utils.OpenApi2 && updated.GetSpecInfo().SpecType == utils.OpenApi2 {
v2ModelLeft, errs := original.BuildV2Model()
if len(errs) > 0 {
errors = errs
}
v2ModelRight, errs := updated.BuildV2Model()
if len(errs) > 0 {
errors = errs
}
if len(errors) > 0 {
return nil, errors
}
return what_changed.CompareSwaggerDocuments(v2ModelLeft.Model.GoLow(), v2ModelRight.Model.GoLow()), nil
}
return nil, nil
}
94 changes: 94 additions & 0 deletions document_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,100 @@ info:
// url: https://pb33f.io/license
}

func ExampleCompareDocuments_openAPI() {

// How to compare two different OpenAPI specifications.

// load an original OpenAPI 3 specification from bytes
burgerShopOriginal, _ := ioutil.ReadFile("test_specs/burgershop.openapi.yaml")

// load an **updated** OpenAPI 3 specification from bytes
burgerShopUpdated, _ := ioutil.ReadFile("test_specs/burgershop.openapi-modified.yaml")

// create a new document from original specification bytes
originalDoc, err := NewDocument(burgerShopOriginal)

// if anything went wrong, an error is thrown
if err != nil {
panic(fmt.Sprintf("cannot create new document: %e", err))
}

// create a new document from updated specification bytes
updatedDoc, err := NewDocument(burgerShopUpdated)

// if anything went wrong, an error is thrown
if err != nil {
panic(fmt.Sprintf("cannot create new document: %e", err))
}

// Compare documents for all changes made
documentChanges, errs := CompareDocuments(originalDoc, updatedDoc)

// If anything went wrong when building models for documents.
if len(errs) > 0 {
for i := range errs {
fmt.Printf("error: %e\n", errs[i])
}
panic(fmt.Sprintf("cannot compare documents: %d errors reported", len(errs)))
}

// Extract SchemaChanges from components changes.
schemaChanges := documentChanges.ComponentsChanges.SchemaChanges

// Print out some interesting stats about the OpenAPI document changes.
fmt.Printf("There are %d changes, of which %d are breaking. %v schemas have changes.",
documentChanges.TotalChanges(), documentChanges.TotalBreakingChanges(), len(schemaChanges))
//Output: There are 67 changes, of which 17 are breaking. 5 schemas have changes.

}

func ExampleCompareDocuments_swagger() {

// How to compare two different Swagger specifications.

// load an original OpenAPI 3 specification from bytes
petstoreOriginal, _ := ioutil.ReadFile("test_specs/petstorev2-complete.yaml")

// load an **updated** OpenAPI 3 specification from bytes
petstoreUpdated, _ := ioutil.ReadFile("test_specs/petstorev2-complete-modified.yaml")

// create a new document from original specification bytes
originalDoc, err := NewDocument(petstoreOriginal)

// if anything went wrong, an error is thrown
if err != nil {
panic(fmt.Sprintf("cannot create new document: %e", err))
}

// create a new document from updated specification bytes
updatedDoc, err := NewDocument(petstoreUpdated)

// if anything went wrong, an error is thrown
if err != nil {
panic(fmt.Sprintf("cannot create new document: %e", err))
}

// Compare documents for all changes made
documentChanges, errs := CompareDocuments(originalDoc, updatedDoc)

// If anything went wrong when building models for documents.
if len(errs) > 0 {
for i := range errs {
fmt.Printf("error: %e\n", errs[i])
}
panic(fmt.Sprintf("cannot compare documents: %d errors reported", len(errs)))
}

// Extract SchemaChanges from components changes.
schemaChanges := documentChanges.ComponentsChanges.SchemaChanges

// Print out some interesting stats about the Swagger document changes.
fmt.Printf("There are %d changes, of which %d are breaking. %v schemas have changes.",
documentChanges.TotalChanges(), documentChanges.TotalBreakingChanges(), len(schemaChanges))
//Output: There are 52 changes, of which 27 are breaking. 5 schemas have changes.

}

func TestDocument_Paths_As_Array(t *testing.T) {

// paths can now be wrapped in an array.
Expand Down
29 changes: 29 additions & 0 deletions what-changed/what_changed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package what_changed

import (
"fmt"
"github.com/pb33f/libopenapi/datamodel"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
Expand Down Expand Up @@ -124,3 +125,31 @@ func Benchmark_CompareStripe(b *testing.B) {
CompareOpenAPIDocuments(origDoc, modDoc)
}
}

func ExampleCompareOpenAPIDocuments() {

// Read in a 'left' (original) OpenAPI specification
original, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")

// Read in a 'right' (modified) OpenAPI specification
modified, _ := ioutil.ReadFile("../test_specs/burgershop.openapi-modified.yaml")

// Extract SpecInfo from bytes
infoOriginal, _ := datamodel.ExtractSpecInfo(original)
infoModified, _ := datamodel.ExtractSpecInfo(modified)

// Build OpenAPI Documents from SpecInfo
origDocument, _ := v3.CreateDocument(infoOriginal)
modDocDocument, _ := v3.CreateDocument(infoModified)

// Compare OpenAPI Documents and extract to *DocumentChanges
changes := CompareOpenAPIDocuments(origDocument, modDocDocument)

// Extract SchemaChanges from components changes.
schemaChanges := changes.ComponentsChanges.SchemaChanges

// Print out some interesting stats.
fmt.Printf("There are %d changes, of which %d are breaking. %v schemas have changes.",
changes.TotalChanges(), changes.TotalBreakingChanges(), len(schemaChanges))
//Output: There are 67 changes, of which 17 are breaking. 5 schemas have changes.
}

0 comments on commit cd7f24f

Please sign in to comment.