Skip to content

Commit

Permalink
Merge pull request #25 from n3wscott/ce-diff
Browse files Browse the repository at this point in the history
add cloudevent diff command
  • Loading branch information
n3wscott authored Mar 7, 2022
2 parents 08d5cb1 + 312bd2c commit 4e495fa
Show file tree
Hide file tree
Showing 34 changed files with 1,568 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/go-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
name: Build
strategy:
matrix:
go-version: [1.14.x, 1.15.x, 1.16.x]
go-version: [1.16.x, 1.17.x]
platform: [ubuntu-latest]

runs-on: ${{ matrix.platform }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/go-format.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ jobs:

steps:

- name: Setup Go 1.16.x
- name: Setup Go 1.17.x
uses: actions/setup-go@v2
with:
go-version: 1.16.x
go-version: 1.17.x

- name: Checkout code
uses: actions/checkout@v2
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/go-lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ jobs:

steps:

- name: Setup Go 1.16.x
- name: Setup Go 1.17.x
uses: actions/setup-go@v2
with:
go-version: 1.16.x
go-version: 1.17.x

- name: Checkout code
uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/go-unit-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
name: Unit Test
strategy:
matrix:
go-version: [1.14.x, 1.15.x, 1.16.x]
go-version: [1.16.x, 1.17.x]
platform: [ubuntu-latest]

runs-on: ${{ matrix.platform }}
Expand Down
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,44 @@ Optionally, you can forward the incoming request to a target.
cloudevents listen -v -t http://localhost:8181 > got.yaml
```

### Diff

`diff` compares two yaml event files.

```shell script
cloudevents diff ./want.yaml ./got.yaml
```

`want.yaml` could have fewer fields specified to allow for fuzzy matching.

Example, if you only wanted to compare on `type` and ignore additional fields:

```shell script
$ cat ./want.yaml
ContextAttributes:
type: com.example.someevent
$ cat ./got.yaml
Mode: structured
ContextAttributes:
specversion: 1.0
type: com.example.someevent
time: 2018-04-05T03:56:24Z
id: 4321-4321-4321-a
source: /mycontext/subcontext
Extensions:
comexampleextension1 : "value"
comexampleextension2 : |
{"othervalue": 5}
TransportExtensions:
user-agent: "foo"
Data: |
{"world":"hello"}

$ cloudevents diff ./want.yaml ./got.yaml --match type --ignore-additions
```

This validates that at least one event of type `com.example.someevent` is present in the `got.yaml` file.

## Advanced Usage

If you would like to produce a pre-produced event yaml file, you can use
Expand Down
1 change: 1 addition & 0 deletions cmd/cloudevents/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func main() {
Run: func(cmd *cobra.Command, args []string) {
_ = cmd.Help()
},
SilenceUsage: true,
}
commands.AddConformanceCommands(cmds)

Expand Down
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
module github.com/cloudevents/conformance

go 1.14
go 1.16

require (
github.com/spf13/cobra v0.0.5
github.com/google/go-cmp v0.5.7
github.com/kylelemons/godebug v1.1.0
github.com/spf13/cobra v1.3.0
gopkg.in/yaml.v2 v2.4.0
)
775 changes: 753 additions & 22 deletions go.sum

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pkg/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ func AddConformanceCommands(topLevel *cobra.Command) {
addInvoke(topLevel)
addListener(topLevel)
addRaw(topLevel)
addDiff(topLevel)
}
45 changes: 45 additions & 0 deletions pkg/commands/diff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
Copyright 2022 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/

package commands

import (
"os"

"github.com/spf13/cobra"

"github.com/cloudevents/conformance/pkg/commands/options"
"github.com/cloudevents/conformance/pkg/diff"
)

func addDiff(topLevel *cobra.Command) {
do := &options.DiffOptions{}

cmd := &cobra.Command{
Use: "diff file_a file_b",
Short: "Compare differences between two sets of event data.",
Example: `
cloudevents diff ./want.yaml ./got.yaml
`,
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
r := diff.Diff{
Out: cmd.OutOrStdout(),
FileA: args[0],
FileB: args[1],
FindBy: do.FindBy,
FullDiff: do.FullDiff,
IgnoreAdditions: do.IgnoreAdditions,
}
if err := r.Do(); err != nil {
os.Exit(1)
}
},
}

do.AddFlags(cmd)

topLevel.AddCommand(cmd)
}
2 changes: 1 addition & 1 deletion pkg/commands/listen.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func addListener(topLevel *cobra.Command) {
}

// Run it.
if err := i.Do(); err != nil {
if err := i.Do(cmd.Context()); err != nil {
log.Fatalf("error listening: %v", err)
}
},
Expand Down
27 changes: 27 additions & 0 deletions pkg/commands/options/diff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Copyright 2022 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/

package options

import "github.com/spf13/cobra"

// DiffOptions
type DiffOptions struct {
FindBy []string

FullDiff bool
IgnoreAdditions bool
}

func (o *DiffOptions) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringArrayVar(&o.FindBy, "match", []string{"id"},
"Find by keys to compare each event set.")

cmd.Flags().BoolVar(&o.FullDiff, "full", false,
"Print the full diff.")

cmd.Flags().BoolVar(&o.IgnoreAdditions, "ignore-additions", false,
"Ignore additions between file_a and file_b.")
}
143 changes: 143 additions & 0 deletions pkg/diff/diff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
Copyright 2022 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/

package diff

import (
"errors"
"fmt"
"io"
"strings"

"github.com/cloudevents/conformance/pkg/event"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
cmpdiff "github.com/kylelemons/godebug/diff"
)

// Diff compares two CloudEvents yaml files (or directories) for differences,
// ignoring `Mode` and `TransportExtensions`.
type Diff struct {
Out io.Writer

FindBy []string
FullDiff bool
IgnoreAdditions bool

FileA string
FileB string
}

func (i *Diff) find(like event.Event, all []event.Event) (string, *event.Event) {
label := ""
for _, b := range i.FindBy {
if len(label) == 0 {
label = fmt.Sprintf("%s[%s]", b, like.Get(b))
} else {
label = fmt.Sprintf("%s %s[%s]", label, b, like.Get(b))
}
}

// Look for the matching ID, Source and Subject.
for _, a := range all {
match := true
for _, key := range i.FindBy {
if a.Get(key) != like.Get(key) {
match = false
break
}
}
if match {
return label, &a
}
}
return label, nil
}

func (i *Diff) Do() error {
ignoreOpts := cmpopts.IgnoreFields(event.Event{}, "Mode", "TransportExtensions")

eventsA, err := event.FromYaml(i.FileA, true)
if err != nil {
return err
}

eventsB, err := event.FromYaml(i.FileB, true)
if err != nil {
return err
}

var diffs []string

for _, a := range eventsA {
label, b := i.find(a, eventsB)

if b == nil {
_, _ = fmt.Fprintf(i.Out, "missing: %s\n", label)
diffs = append(diffs, fmt.Sprintf("missing: %s\n", label))
continue
}

if diff := cmp.Diff(a, b, ignoreOpts); diff != "" {
// Clear out Mode and Transport Extensions.
a.Mode = ""
b.Mode = ""
a.TransportExtensions = nil
b.TransportExtensions = nil

ab, err := event.ToYaml(a)
if err != nil {
return err
}
bb, err := event.ToYaml(*b)
if err != nil {
return err
}

ignore := true
sb := &strings.Builder{}
if len(diffs) >= 1 {
sb.WriteString("---\n")
}
sb.WriteString(fmt.Sprintf("%s diffs (-a, +b):\n", label))

chunks := cmpdiff.DiffChunks(strings.Split(string(ab), "\n"), strings.Split(string(bb), "\n"))
changed := map[string]bool{}
for _, c := range chunks {
if len(c.Added) != 0 {
for _, a := range c.Added {
if _, found := changed[strings.Split(a, ":")[0]]; found || !i.IgnoreAdditions {
ignore = false
sb.WriteString(fmt.Sprintf("+ %s\n", a))
}
}
}
if len(c.Deleted) != 0 {
ignore = false
for _, d := range c.Deleted {
sb.WriteString(fmt.Sprintf("- %s\n", d))
changed[strings.Split(d, ":")[0]] = true
}
}
}

if i.FullDiff {
_, _ = fmt.Fprintf(i.Out, "%s\n", cmpdiff.Diff(string(ab), string(bb)))
} else if !ignore {
_, _ = fmt.Fprint(i.Out, sb.String())
if len(diffs) > 0 {
diffs = append(diffs, "---")
}
diffs = append(diffs, cmpdiff.Diff(string(ab), string(bb)))
}
}
}

if len(diffs) > 0 {
return errors.New(strings.Join(diffs, "\n"))
}

return nil
}
Loading

0 comments on commit 4e495fa

Please sign in to comment.