Skip to content

Commit

Permalink
allow more control of filtering Reply errors (#61)
Browse files Browse the repository at this point in the history
The `Reply.Err()` and `RPCErrors.Filter()` are useful functions to get
the `SevError` level errors out, but some people may want to treat
`SevWarning` as errors as well and doing it manually is a pain.

As not to add a bunch of alternative functions like
`RPCError.FilterWarn`, `Reply.ErrWarn()` and `Reply.ErrAll()` this
updates `Filter` and `Err` to take in a list of desired severity levels.
If they are missing they go back to defaulting to `SevError` which is a
sane default.

## Breaking change:

The API for `reply.Err()` and `RPCErrors.Filter()` were changed as
above. For most call sites this should be fine but anyone using this
function in a interface or explicitly using it as a type will break.
`ErrSevError` was renamed to `SevError`
`ErrSevWarning` was reanmed to `SevWarning`

As the API is not yet stable these changes are ok and will become
stabilized after 1.0.0
  • Loading branch information
nemith authored Aug 29, 2023
1 parent 4686b20 commit becda36
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 10 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
golang.org/x/sys v0.11.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
Expand Down
50 changes: 41 additions & 9 deletions msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"strings"
"time"

"golang.org/x/exp/slices"
)

// RawXML captures the raw xml for the given element. Used to process certain
Expand Down Expand Up @@ -76,10 +78,36 @@ func (r Reply) Decode(v interface{}) error {
return xml.Unmarshal(r.Body, v)
}

// Err will return the error(s) from a Reply that are severity of `error` or
// higher.
func (r Reply) Err() error {
errs := r.Errors.Filter()
// Err will return go error(s) from a Reply that are of the given severities. If
// no severity is given then it defaults to `ErrSevError`.
//
// If one error is present then the underlyign type is `RPCError`. If more than
// one error exists than the underlying type is `[]RPCError`
//
// Example

// get all errors with severity of error
//
// if err := reply.Err(ErrSevError); err != nil { /* ... */ }
//
// or
//
// if err := reply.Err(); err != nil { /* ... */ }
//
// get all errors with severity of only warning
//
// if err := reply.Err(ErrSevWarning); err != nil { /* ... */ }
//
// get all errors
//
// if err := reply.Err(ErrSevWarning, ErrSevError); err != nil { /* ... */ }
func (r Reply) Err(severity ...ErrSeverity) error {
// fast escape for no errors
if len(r.Errors) == 0 {
return nil
}

errs := r.Errors.Filter(severity...)
switch len(errs) {
case 0:
return nil
Expand All @@ -105,8 +133,8 @@ func (r Notification) Decode(v interface{}) error {
type ErrSeverity string

const (
ErrSevError ErrSeverity = "error"
ErrSevWarning ErrSeverity = "warning"
SevError ErrSeverity = "error"
SevWarning ErrSeverity = "warning"
)

type ErrType string
Expand Down Expand Up @@ -154,19 +182,23 @@ type RPCError struct {
}

func (e RPCError) Error() string {
return fmt.Sprintf("rpc error: %s", e.Message)
return fmt.Sprintf("netconf error: %s %s: %s", e.Type, e.Tag, e.Message)
}

type RPCErrors []RPCError

func (errs RPCErrors) Filter() RPCErrors {
func (errs RPCErrors) Filter(severity ...ErrSeverity) RPCErrors {
if len(errs) == 0 {
return nil
}

if len(severity) == 0 {
severity = []ErrSeverity{SevError}
}

filteredErrs := make(RPCErrors, 0, len(errs))
for _, err := range errs {
if err.Severity == ErrSevWarning {
if !slices.Contains(severity, err.Severity) {
continue
}
filteredErrs = append(filteredErrs, err)
Expand Down
2 changes: 1 addition & 1 deletion msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ func TestUnmarshalRPCReply(t *testing.T) {
{
Type: ErrTypeProtocol,
Tag: ErrOperationFailed,
Severity: ErrSevError,
Severity: SevError,
Message: "syntax error, expecting <candidate/> or <running/>",
Info: []byte(`
<bad-element>non-exist</bad-element>
Expand Down

0 comments on commit becda36

Please sign in to comment.