...

Source file src/edge-infra.dev/pkg/edge/api/apierror/bsl/bslerror.go

Documentation: edge-infra.dev/pkg/edge/api/apierror/bsl

     1  package bsl
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"regexp"
     8  )
     9  
    10  const (
    11  	// constraintViolationErr error type returned by BSL when the request body is not in the acceptable format.
    12  	constraintViolationErr = "com.ncr.nep.common.exception.PayloadConstraintViolationException"
    13  	// tenantAccessDeniedErr error type returned by BSL when the user does not have access to the requested organization (or sub organization).
    14  	tenantAccessDeniedErr = "com.ncr.nep.common.exception.TenantAccessDeniedException"
    15  	// accessDeniedErr error type returned by BSL when the user is not authorized to access that resource.
    16  	accessDeniedErr = "com.ncr.nep.common.exception.AccessDeniedException"
    17  	// invalidCredentialsErr error type returned by BSL when the user credentials supplied is invalid.
    18  	invalidCredentialsErr = "com.ncr.nep.common.exception.InvalidCredentialsException" //nolint:gosec
    19  	// resourceNotExistsErr error type returned when the requested resource does not exists or has been deleted.
    20  	resourceNotExistsErr = "com.ncr.nep.common.exception.ResourceDoesNotExistException"
    21  	// resourceAlreadyExistsErr error type returned when the requested already exists.
    22  	resourceAlreadyExistsErr = "com.ncr.nep.common.exception.ResourceAlreadyExistsException"
    23  	// inactiveAccountErr error type returned when the user account has been deactivated or status changed to inactive.
    24  	inactiveAccountErr = "com.ncr.nep.security.authn.AccountInactiveException"
    25  	// lockedAccountErr error type returned when the user account has been locked from more than 5 invalid password tries.
    26  	lockedAccountErr = "com.ncr.nep.security.authn.AccountLockedException"
    27  	// businessConstraintViolationErr error type returned similar to the constraintViolationErr.
    28  	businessConstraintViolationErr = "com.ncr.nep.common.exception.BusinessConstraintViolationException"
    29  	// genericErr error type returned when the client request is invalid or incomplete.
    30  	genericErr = "javax.ws.rs.ClientErrorException"
    31  )
    32  
    33  var (
    34  	// wellKnownErrorTypes maps the BSL error types to Edge Client readable messages.
    35  	wellKnownErrorTypes = map[string]string{
    36  		tenantAccessDeniedErr:          "Access Denied, Tenant cannot access resource",
    37  		accessDeniedErr:                "Access Denied, Authorization Required",
    38  		invalidCredentialsErr:          "Incorrect username, password, or organization.",
    39  		resourceNotExistsErr:           "Resource does not exist",
    40  		resourceAlreadyExistsErr:       "the resources already exists",
    41  		constraintViolationErr:         "Constraint violation", //can be returned when the resource already exists, check for conflict 409 statusCode too
    42  		inactiveAccountErr:             "Your account is inactive. Contact your organization admin to activate your account.",
    43  		lockedAccountErr:               "Your account has been locked. Contact your organization admin to unlock your account.",
    44  		businessConstraintViolationErr: "Invalid Value supplied",
    45  		genericErr:                     "Client Error Exception",
    46  	}
    47  )
    48  
    49  // Error represents the BSL error.
    50  type Error struct {
    51  	Err        error
    52  	Message    string
    53  	StatusCode int
    54  	Verbose    string
    55  	ErrorType  string
    56  	URL        string
    57  	Path       string
    58  	Method     string
    59  }
    60  
    61  // ErrorResponse represents an error body response from BSL.
    62  type ErrorResponse struct {
    63  	StatusCode           int                   `json:"statusCode"`
    64  	Message              string                `json:"message"`
    65  	ErrorType            string                `json:"errorType"`
    66  	Details              []string              `json:"details"`
    67  	ConstraintViolations []ConstraintViolation `json:"constraintViolations"`
    68  }
    69  
    70  // ConstraintViolation is returned when the BSL error type is com.ncr.nep.common.exception.PayloadConstraintViolationException.
    71  type ConstraintViolation struct {
    72  	InvalidValue string `json:"invalidValue"`
    73  	Message      string `json:"message"`
    74  	PropertyPath string `json:"propertyPath"`
    75  }
    76  
    77  // New returns a new BSL Error.
    78  func New(msg string) *Error {
    79  	return Wrap(errors.New(msg))
    80  }
    81  
    82  // Wrap returns a new BSL Error from a stdlib error.
    83  func Wrap(err error) *Error {
    84  	if err == nil {
    85  		return &Error{}
    86  	}
    87  	msg := err.Error()
    88  	if msg == "" {
    89  		msg = "Internal Server Error"
    90  	}
    91  	return &Error{
    92  		Err:     err,
    93  		Message: msg,
    94  	}
    95  }
    96  
    97  // SetMessage sets the message field of the BSL error.
    98  func (b *Error) SetMessage(msg string) *Error {
    99  	b.Message = msg
   100  	return b
   101  }
   102  
   103  // SetStatusCode sets the status code of the BSL error.
   104  func (b *Error) SetStatusCode(code int) *Error {
   105  	b.StatusCode = code
   106  	return b
   107  }
   108  
   109  // SetURL sets the url of the BSL error.
   110  func (b *Error) SetURL(url string) *Error {
   111  	b.URL = url
   112  	return b
   113  }
   114  
   115  // SetPath sets the path field of the BSL error.
   116  func (b *Error) SetPath(path string) *Error {
   117  	b.Path = path
   118  	return b
   119  }
   120  
   121  // SetMethod sets the method field of the BSL error.
   122  func (b *Error) SetMethod(method string) *Error {
   123  	b.Method = method
   124  	return b
   125  }
   126  
   127  // SetErrorType sets the error type of the BSL error.
   128  func (b *Error) SetErrorType(errorType string) *Error {
   129  	b.ErrorType = errorType
   130  	return b
   131  }
   132  
   133  // SetVerbose sets the verbose field with the entire error response returned by BSL.
   134  func (b *Error) SetVerbose(verbose string) *Error {
   135  	b.Verbose = verbose
   136  	return b
   137  }
   138  
   139  // Error implements the error interface and returns the error message of the BSL error.
   140  func (b *Error) Error() string {
   141  	return b.Message
   142  }
   143  
   144  // Unwrap converts a BSL error to an stdlib error.
   145  func (b *Error) Unwrap() error {
   146  	if b != nil {
   147  		return b.Err
   148  	}
   149  	return nil
   150  }
   151  
   152  // UnmarshalErrorResponse unmarshals the error response body from BSL and sets the appropriate error message,
   153  // verbose, status code and error type.
   154  func (b *Error) UnmarshalErrorResponse(body []byte) *Error {
   155  	if len(body) > 0 {
   156  		bslErrResponse := &ErrorResponse{}
   157  		err := json.Unmarshal(body, &bslErrResponse)
   158  		if err != nil {
   159  			b.Message = fmt.Sprintf("an error occurred unmarshaling json response. %s", err.Error())
   160  			return b
   161  		}
   162  		if val, exists := wellKnownErrorTypes[bslErrResponse.ErrorType]; exists {
   163  			switch val {
   164  			case wellKnownErrorTypes[constraintViolationErr], wellKnownErrorTypes[businessConstraintViolationErr], wellKnownErrorTypes[resourceNotExistsErr]:
   165  				msg := stripSymbols(bslErrResponse.Message)
   166  				if msg == "" {
   167  					b.Message = val
   168  					break
   169  				}
   170  				b.Message = msg
   171  			default:
   172  				b.Message = val
   173  			}
   174  		}
   175  		return b.SetStatusCode(bslErrResponse.StatusCode).SetErrorType(bslErrResponse.ErrorType).SetVerbose(string(body))
   176  	}
   177  	return b
   178  }
   179  
   180  // Extensions builds the error extensions that are displayed in the additional field in the graphql error response.
   181  func (b *Error) Extensions() map[string]interface{} {
   182  	return map[string]interface{}{
   183  		"statusCode": b.StatusCode,
   184  		//"verbose":    b.Verbose, don't want expose implementation details
   185  		"method":    b.Method,
   186  		"path":      b.Path,
   187  		"url":       b.URL,
   188  		"errorType": b.ErrorType,
   189  	}
   190  }
   191  
   192  // Is checks if a BSL error and an stdlib error are equal.
   193  func (b *Error) Is(target error) bool {
   194  	if target == nil {
   195  		return false
   196  	}
   197  
   198  	return b.Message == target.Error()
   199  }
   200  
   201  // IsError checks if two BSL errors are equal.
   202  func (b *Error) IsError(target *Error) bool {
   203  	return b.Message == target.Message && b.Method == target.Method && b.ErrorType == target.ErrorType && b.StatusCode == target.StatusCode && b.URL == target.URL && b.Path == target.Path
   204  }
   205  
   206  // stripSymbols strips the [ or ] symbols that BSL adds to error messages sometimes :(
   207  func stripSymbols(message string) string {
   208  	reg, err := regexp.Compile(`[\[/]|[\]/]`)
   209  	if err != nil {
   210  		return "Internal Server Error"
   211  	}
   212  	return string(reg.ReplaceAll([]byte(message), []byte("")))
   213  }
   214  

View as plain text