From aca125aa2b09b8e6e4e7fbbda1c62eaf482eb9b8 Mon Sep 17 00:00:00 2001 From: Waldemar Quevedo Date: Sun, 14 Aug 2022 00:21:29 -0700 Subject: [PATCH] js: leverage more errors package internals Signed-off-by: Waldemar Quevedo --- jsm.go | 32 +++++++++++++++++++++++++++-- nats.go | 2 +- test/js_test.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/jsm.go b/jsm.go index e6bb09006..3bc3535cb 100644 --- a/jsm.go +++ b/jsm.go @@ -161,19 +161,36 @@ type APIError struct { Description string `json:"description,omitempty"` } +// Error prints the JetStream API error code and description +func (e *APIError) Error() string { + return fmt.Sprintf("nats: API error %d: %s", e.ErrorCode, e.Description) +} + +// Is matches against an APIError. +func (e *APIError) Is(err error) bool { + // Extract internal APIError to match against. + var aerr *APIError + ok := errors.As(err, &aerr) + if !ok { + return ok + } + return e.ErrorCode == aerr.ErrorCode +} + // JetStreamAPIError is an error result from making a request to the // JetStream API. type JetStreamAPIError interface { Code() int ErrorCode() int Description() string - Error() string + error } type jsAPIError struct { code int errorCode int description string + message string } func (err *jsAPIError) Code() int { @@ -185,11 +202,22 @@ func (err *jsAPIError) ErrorCode() int { } func (err *jsAPIError) Description() string { + if err.description == "" { + return err.message + } return err.description } func (err *jsAPIError) Error() string { - return err.description + return fmt.Sprintf("nats: %v", err.message) +} + +func (err *jsAPIError) Unwrap() error { + return &APIError{ + Code: err.Code(), + ErrorCode: err.ErrorCode(), + Description: err.Description(), + } } // apiResponse is a standard response from the JetStream JSON API diff --git a/nats.go b/nats.go index 9f26280b8..836ae17c6 100644 --- a/nats.go +++ b/nats.go @@ -173,7 +173,7 @@ var ( var ( // ErrJetStreamNotEnabled is an error returned when JetStream is not enabled for an account. - ErrJetStreamNotEnabled JetStreamAPIError = &jsAPIError{errorCode: 10039, description: "nats: jetstream not enabled"} + ErrJetStreamNotEnabled JetStreamAPIError = &jsAPIError{errorCode: 10039, message: "jetstream not enabled"} ) func init() { diff --git a/test/js_test.go b/test/js_test.go index 2b6fd4a3b..b739510c2 100644 --- a/test/js_test.go +++ b/test/js_test.go @@ -88,9 +88,24 @@ func TestJetStreamNotAccountEnabled(t *testing.T) { defer nc.Close() _, err := js.AccountInfo() + + // check directly to var (backwards compatible) if err != nats.ErrJetStreamNotEnabled { t.Fatalf("Did not get the proper error, got %v", err) } + + // matching via errors.Is + if ok := errors.Is(err, nats.ErrJetStreamNotEnabled); !ok { + t.Fatal("Expected ErrJetStreamNotEnabled") + } + + // matching wrapped via error.Is + err2 := fmt.Errorf("custom error: %w", nats.ErrJetStreamNotEnabled) + if ok := errors.Is(err2, nats.ErrJetStreamNotEnabled); !ok { + t.Fatal("Expected wrapped ErrJetStreamNotEnabled") + } + + // via classic type assertion. jserr, ok := err.(nats.JetStreamAPIError) if !ok { t.Fatal("Expected a JetStreamAPIError") @@ -99,6 +114,44 @@ func TestJetStreamNotAccountEnabled(t *testing.T) { if jserr.ErrorCode() != expected { t.Fatalf("Expected: %v, got: %v", expected, jserr.ErrorCode()) } + + // matching to interface via errors.As(...) + var apierr nats.JetStreamAPIError + ok = errors.As(err, &apierr) + if !ok { + t.Fatal("Expected a JetStreamAPIError") + } + if apierr.ErrorCode() != expected { + t.Fatalf("Expected: %v, got: %v", expected, apierr.ErrorCode()) + } + expectedMessage := "nats: jetstream not enabled" + if apierr.Error() != expectedMessage { + t.Fatalf("Expected: %v, got: %v", expectedMessage, apierr.Error()) + } + + // matching arbitrary custom error via errors.Is(...) + customErr := &nats.APIError{ErrorCode: expected} + if ok := errors.Is(customErr, nats.ErrJetStreamNotEnabled); !ok { + t.Fatal("Expected wrapped ErrJetStreamNotEnabled") + } + customErr = &nats.APIError{ErrorCode: 1} + if ok := errors.Is(customErr, nats.ErrJetStreamNotEnabled); ok { + t.Fatal("Expected to not match ErrJetStreamNotEnabled") + } + + // matching to concrete type via errors.As(...) + var aerr *nats.APIError + ok = errors.As(err, &aerr) + if !ok { + t.Fatal("Expected an APIError") + } + if aerr.ErrorCode != expected { + t.Fatalf("Expected: %v, got: %v", expected, aerr.ErrorCode) + } + expectedMessage = "nats: API error 10039: jetstream not enabled" + if aerr.Error() != expectedMessage { + t.Fatalf("Expected: %v, got: %v", expectedMessage, apierr.Error()) + } } func TestJetStreamPublish(t *testing.T) {