Skip to content

Make status code and NGINX error codes on internalError publicly accessible #489

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions client/nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ var (
ErrPlusVersionNotFound = errors.New("plus version not found in the input string")
)

// StatusError is an interface that defines our API with consumers of the plus client errors.
// The error will return a http status code and an NGINX error code.
type StatusError interface {
Status() int
Code() string
}

var _ StatusError = (*internalError)(nil)

// NginxClient lets you access NGINX Plus API.
type NginxClient struct {
httpClient *http.Client
Expand Down Expand Up @@ -112,8 +121,18 @@ type apiError struct {
}

type internalError struct {
err string
apiError
err string
apiError apiError
}

// Status returns the HTTP status code of the error.
func (internalError *internalError) Status() int {
return internalError.apiError.Status
}

// Status returns the NGINX error code on the response.
func (internalError *internalError) Code() string {
return internalError.apiError.Code
}

// Error allows internalError to match the Error interface.
Expand Down Expand Up @@ -1782,7 +1801,7 @@ func (client *NginxClient) GetStreamServerZones(ctx context.Context) (*StreamSer
if err != nil {
var ie *internalError
if errors.As(err, &ie) {
if ie.Code == pathNotFoundCode {
if ie.Code() == pathNotFoundCode {
return &zones, nil
}
}
Expand All @@ -1808,7 +1827,7 @@ func (client *NginxClient) GetStreamUpstreams(ctx context.Context) (*StreamUpstr
if err != nil {
var ie *internalError
if errors.As(err, &ie) {
if ie.Code == pathNotFoundCode {
if ie.Code() == pathNotFoundCode {
return &upstreams, nil
}
}
Expand All @@ -1824,7 +1843,7 @@ func (client *NginxClient) GetStreamZoneSync(ctx context.Context) (*StreamZoneSy
if err != nil {
var ie *internalError
if errors.As(err, &ie) {
if ie.Code == pathNotFoundCode {
if ie.Code() == pathNotFoundCode {
return nil, nil
}
}
Expand Down Expand Up @@ -2137,7 +2156,7 @@ func (client *NginxClient) GetStreamConnectionsLimit(ctx context.Context) (*Stre
if err != nil {
var ie *internalError
if errors.As(err, &ie) {
if ie.Code == pathNotFoundCode {
if ie.Code() == pathNotFoundCode {
return &limitConns, nil
}
}
Expand Down
65 changes: 65 additions & 0 deletions client/nginx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package client
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"reflect"
Expand Down Expand Up @@ -1438,6 +1439,70 @@ func TestUpdateStreamServers(t *testing.T) {
}
}

func TestInternalError(t *testing.T) {
t.Parallel()

// mimic a user-defined interface type
type TestStatusError interface {
Status() int
Code() string
}

//nolint // ignore golangci-lint err113 sugggestion to create package level static error
anotherErr := errors.New("another error")

notFoundErr := &internalError{
err: "not found error",
apiError: apiError{
Text: "not found error",
Status: http.StatusNotFound,
Code: "not found code",
},
}

testcases := map[string]struct {
inputErr error
expectedCode string
expectedStatus int
}{
"simple not found": {
inputErr: notFoundErr,
expectedStatus: http.StatusNotFound,
expectedCode: "not found code",
},
"not found joined with another error": {
inputErr: errors.Join(notFoundErr, anotherErr),
expectedStatus: http.StatusNotFound,
expectedCode: "not found code",
},
"not found wrapped with another error": {
inputErr: notFoundErr.Wrap("some error"),
expectedStatus: http.StatusNotFound,
expectedCode: "not found code",
},
}

for name, tc := range testcases {
t.Run(name, func(t *testing.T) {
t.Parallel()

var se TestStatusError
ok := errors.As(tc.inputErr, &se)
if !ok {
t.Fatalf("could not cast error %v as StatusError", tc.inputErr)
}

if se.Status() != tc.expectedStatus {
t.Fatalf("expected status %d, got status %d", tc.expectedStatus, se.Status())
}

if se.Code() != tc.expectedCode {
t.Fatalf("expected code %s, got code %s", tc.expectedCode, se.Code())
}
})
}
}

type response struct {
servers interface{}
statusCode int
Expand Down
Loading