github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/pkg/apperrors/errors.go (about)

     1  package apperrors
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
    10  )
    11  
    12  // Error missing godoc
    13  type Error struct {
    14  	errorCode ErrorType
    15  	Message   string
    16  	arguments map[string]string
    17  	parentErr error
    18  }
    19  
    20  // Error missing godoc
    21  func (e Error) Error() string {
    22  	builder := strings.Builder{}
    23  	builder.WriteString(e.Message)
    24  
    25  	var i = 0
    26  	if len(e.arguments) != 0 {
    27  		builder.WriteString(" [")
    28  		keys := sortMapKey(e.arguments)
    29  		for _, key := range keys {
    30  			builder.WriteString(fmt.Sprintf("%s=%s", key, e.arguments[key]))
    31  			i++
    32  			if len(e.arguments) != i {
    33  				builder.WriteString("; ")
    34  			}
    35  		}
    36  		builder.WriteString("]")
    37  	}
    38  	if e.errorCode == InternalError && e.parentErr != nil {
    39  		builder.WriteString(": ")
    40  		builder.WriteString(e.parentErr.Error())
    41  	}
    42  
    43  	return builder.String()
    44  }
    45  
    46  // Is missing godoc
    47  func (e Error) Is(err error) bool {
    48  	if customErr, ok := err.(Error); ok {
    49  		return e.errorCode == customErr.errorCode
    50  	}
    51  	return false
    52  }
    53  
    54  // ErrorCode missing godoc
    55  func ErrorCode(err error) ErrorType {
    56  	var customErr Error
    57  	found := errors.As(err, &customErr)
    58  	if found {
    59  		return customErr.errorCode
    60  	}
    61  	return UnknownError
    62  }
    63  
    64  // NewNotNullViolationError missing godoc
    65  func NewNotNullViolationError(resourceType resource.Type) error {
    66  	return Error{
    67  		errorCode: EmptyData,
    68  		Message:   EmptyDataMsg,
    69  		arguments: map[string]string{"object": string(resourceType)},
    70  	}
    71  }
    72  
    73  // NewCheckViolationError missing godoc
    74  func NewCheckViolationError(resourceType resource.Type) error {
    75  	return Error{
    76  		errorCode: InconsistentData,
    77  		Message:   InconsistentDataMsg,
    78  		arguments: map[string]string{"object": string(resourceType)},
    79  	}
    80  }
    81  
    82  // NewOperationTimeoutError missing godoc
    83  func NewOperationTimeoutError() error {
    84  	return Error{
    85  		errorCode: OperationTimeout,
    86  		Message:   OperationTimeoutMsg,
    87  	}
    88  }
    89  
    90  // NewNotUniqueError missing godoc
    91  func NewNotUniqueError(resourceType resource.Type) error {
    92  	return Error{
    93  		errorCode: NotUnique,
    94  		Message:   NotUniqueMsg,
    95  		arguments: map[string]string{"object": string(resourceType)},
    96  	}
    97  }
    98  
    99  // NewNotUniqueErrorWithMessage constructs a new NotUniqueError with a custom message
   100  func NewNotUniqueErrorWithMessage(resourceType resource.Type, message string) error {
   101  	return Error{
   102  		errorCode: NotUnique,
   103  		Message:   fmt.Sprintf(NotUniqueMsgF, message),
   104  		arguments: map[string]string{"object": string(resourceType)},
   105  	}
   106  }
   107  
   108  // NewNotUniqueNameError missing godoc
   109  func NewNotUniqueNameError(resourceType resource.Type) error {
   110  	return Error{
   111  		errorCode: NotUniqueName,
   112  		Message:   NotUniqueNameMsg,
   113  		arguments: map[string]string{"object": string(resourceType)},
   114  	}
   115  }
   116  
   117  // NewNotFoundError missing godoc
   118  func NewNotFoundError(resourceType resource.Type, objectID string) error {
   119  	return Error{
   120  		errorCode: NotFound,
   121  		Message:   NotFoundMsg,
   122  		arguments: map[string]string{"object": string(resourceType), "ID": objectID},
   123  		parentErr: nil,
   124  	}
   125  }
   126  
   127  // NewNotFoundErrorWithMessage missing godoc
   128  func NewNotFoundErrorWithMessage(resourceType resource.Type, objectID string, message string) error {
   129  	return Error{
   130  		errorCode: NotFound,
   131  		Message:   fmt.Sprintf(NotFoundMsgF, message),
   132  		arguments: map[string]string{"object": string(resourceType), "ID": objectID},
   133  		parentErr: nil,
   134  	}
   135  }
   136  
   137  // NewNotFoundErrorWithType missing godoc
   138  func NewNotFoundErrorWithType(resourceType resource.Type) error {
   139  	return Error{
   140  		errorCode: NotFound,
   141  		Message:   NotFoundMsg,
   142  		arguments: map[string]string{"object": string(resourceType)},
   143  		parentErr: nil,
   144  	}
   145  }
   146  
   147  // NewInvalidDataError missing godoc
   148  func NewInvalidDataError(msg string, args ...interface{}) error {
   149  	return Error{
   150  		errorCode: InvalidData,
   151  		Message:   InvalidDataMsg,
   152  		arguments: map[string]string{"reason": fmt.Sprintf(msg, args...)},
   153  	}
   154  }
   155  
   156  // NewInvalidDataErrorWithFields missing godoc
   157  func NewInvalidDataErrorWithFields(fields map[string]error, objType string) error {
   158  	if len(fields) == 0 {
   159  		return nil
   160  	}
   161  
   162  	err := Error{
   163  		errorCode: InvalidData,
   164  		Message:   fmt.Sprintf("%s %s", InvalidDataMsg, objType),
   165  		arguments: map[string]string{},
   166  	}
   167  
   168  	for k, v := range fields {
   169  		err.arguments[k] = v.Error()
   170  	}
   171  	return err
   172  }
   173  
   174  // NewInternalError missing godoc
   175  func NewInternalError(msg string, args ...interface{}) error {
   176  	errMsg := fmt.Sprintf(msg, args...)
   177  	return Error{
   178  		errorCode: InternalError,
   179  		Message:   fmt.Sprintf(InternalServerErrMsgF, errMsg),
   180  		arguments: map[string]string{},
   181  	}
   182  }
   183  
   184  // InternalErrorFrom missing godoc
   185  func InternalErrorFrom(err error, msg string, args ...interface{}) error {
   186  	errMsg := fmt.Sprintf(msg, args...)
   187  	return Error{
   188  		errorCode: InternalError,
   189  		Message:   fmt.Sprintf(InternalServerErrMsgF, errMsg),
   190  		arguments: map[string]string{},
   191  		parentErr: err,
   192  	}
   193  }
   194  
   195  // NewTenantNotFoundError missing godoc
   196  func NewTenantNotFoundError(externalTenant string) error {
   197  	return Error{
   198  		errorCode: TenantNotFound,
   199  		Message:   TenantNotFoundMsg,
   200  		arguments: map[string]string{"externalTenant": externalTenant},
   201  	}
   202  }
   203  
   204  // NewTenantRequiredError missing godoc
   205  func NewTenantRequiredError() error {
   206  	return Error{
   207  		errorCode: TenantRequired,
   208  		Message:   TenantRequiredMsg,
   209  		arguments: map[string]string{},
   210  	}
   211  }
   212  
   213  // NewInvalidOperationError missing godoc
   214  func NewInvalidOperationError(reason string) error {
   215  	return Error{
   216  		errorCode: InvalidOperation,
   217  		Message:   InvalidOperationMsg,
   218  		arguments: map[string]string{"reason": reason},
   219  	}
   220  }
   221  
   222  // NewForeignKeyInvalidOperationError missing godoc
   223  func NewForeignKeyInvalidOperationError(sqlOperation resource.SQLOperation, resourceType resource.Type) error {
   224  	var reason string
   225  	switch sqlOperation {
   226  	case resource.Create, resource.Update, resource.Upsert:
   227  		reason = "The referenced entity does not exists"
   228  	case resource.Delete:
   229  		reason = "The record cannot be deleted because another record refers to it"
   230  	}
   231  
   232  	return Error{
   233  		errorCode: InvalidOperation,
   234  		Message:   InvalidOperationMsg,
   235  		arguments: map[string]string{"reason": reason, "object": string(resourceType)},
   236  	}
   237  }
   238  
   239  const valueNotFoundInConfigMsg = "value under specified path not found in configuration"
   240  
   241  // NewValueNotFoundInConfigurationError missing godoc
   242  func NewValueNotFoundInConfigurationError() error {
   243  	return Error{
   244  		errorCode: NotFound,
   245  		Message:   valueNotFoundInConfigMsg,
   246  		arguments: map[string]string{},
   247  	}
   248  }
   249  
   250  // NewNoScopesInContextError missing godoc
   251  func NewNoScopesInContextError() error {
   252  	return Error{
   253  		errorCode: NotFound,
   254  		Message:   NoScopesInContextMsg,
   255  		arguments: map[string]string{},
   256  	}
   257  }
   258  
   259  // NewRequiredScopesNotDefinedError missing godoc
   260  func NewRequiredScopesNotDefinedError() error {
   261  	return Error{
   262  		errorCode: InsufficientScopes,
   263  		Message:   NoRequiredScopesInContextMsg,
   264  		arguments: map[string]string{},
   265  	}
   266  }
   267  
   268  // NewKeyDoesNotExistError missing godoc
   269  func NewKeyDoesNotExistError(key string) error {
   270  	return Error{
   271  		errorCode: NotFound,
   272  		Message:   KeyDoesNotExistMsg,
   273  		arguments: map[string]string{"key": key},
   274  	}
   275  }
   276  
   277  // NewInsufficientScopesError missing godoc
   278  func NewInsufficientScopesError(requiredScopes, actualScopes []string) error {
   279  	return Error{
   280  		errorCode: InsufficientScopes,
   281  		Message:   InsufficientScopesMsg,
   282  		arguments: map[string]string{"required": strings.Join(requiredScopes, ";"),
   283  			"actual": strings.Join(actualScopes, ";")},
   284  		parentErr: nil,
   285  	}
   286  }
   287  
   288  // NewCannotReadTenantError missing godoc
   289  func NewCannotReadTenantError() error {
   290  	return Error{
   291  		errorCode: InternalError,
   292  		Message:   CannotReadTenantMsg,
   293  		arguments: map[string]string{},
   294  	}
   295  }
   296  
   297  // NewCannotReadClientUserError missing godoc
   298  func NewCannotReadClientUserError() error {
   299  	return Error{
   300  		errorCode: InternalError,
   301  		Message:   CannotReadClientUserMsg,
   302  		arguments: map[string]string{},
   303  	}
   304  }
   305  
   306  // NewUnauthorizedError missing godoc
   307  func NewUnauthorizedError(msg string) error {
   308  	return Error{
   309  		errorCode: Unauthorized,
   310  		Message:   UnauthorizedMsg,
   311  		arguments: map[string]string{"reason": msg},
   312  	}
   313  }
   314  
   315  // NewConcurrentOperationInProgressError missing godoc
   316  func NewConcurrentOperationInProgressError(msg string) error {
   317  	return Error{
   318  		errorCode: ConcurrentOperation,
   319  		Message:   ConcurrentOperationMsg,
   320  		arguments: map[string]string{"reason": msg},
   321  	}
   322  }
   323  
   324  // NewInvalidStatusCondition missing godoc
   325  func NewInvalidStatusCondition(resourceType resource.Type) error {
   326  	return Error{
   327  		errorCode: InvalidStatusCondition,
   328  		Message:   InvalidStatusConditionMsg,
   329  		arguments: map[string]string{"object": string(resourceType)},
   330  	}
   331  }
   332  
   333  // NewCannotUpdateObjectInManyBundles missing godoc
   334  func NewCannotUpdateObjectInManyBundles() error {
   335  	return Error{
   336  		errorCode: CannotUpdateObjectInManyBundles,
   337  		Message:   CannotUpdateObjectInManyBundlesMsg,
   338  		arguments: map[string]string{},
   339  	}
   340  }
   341  
   342  // NewConcurrentUpdate returns ConcurrentUpdate error
   343  func NewConcurrentUpdate() error {
   344  	return Error{
   345  		errorCode: ConcurrentUpdate,
   346  		Message:   ConcurrentUpdateMsg,
   347  		arguments: map[string]string{},
   348  	}
   349  }
   350  
   351  // NewCustomErrorWithCode returns Error with a given code and message
   352  func NewCustomErrorWithCode(code int, msg string) error {
   353  	return Error{
   354  		errorCode: ErrorType(code),
   355  		Message:   msg,
   356  		arguments: map[string]string{},
   357  	}
   358  }
   359  
   360  // NewCannotUnassignObjectComingFromASAError returns CannotUnassignRuntimeContextComingFromASAError error
   361  func NewCannotUnassignObjectComingFromASAError(objectID string) error {
   362  	return Error{
   363  		errorCode: InvalidOperation,
   364  		Message:   CannotUnassignObjectFromASA,
   365  		arguments: map[string]string{"ID": objectID},
   366  	}
   367  }
   368  
   369  // IsValueNotFoundInConfiguration missing godoc
   370  func IsValueNotFoundInConfiguration(err error) bool {
   371  	if customErr, ok := err.(Error); ok {
   372  		return customErr.errorCode == NotFound && customErr.Message == valueNotFoundInConfigMsg
   373  	}
   374  	return false
   375  }
   376  
   377  // IsKeyDoesNotExist missing godoc
   378  func IsKeyDoesNotExist(err error) bool {
   379  	if customErr, ok := err.(Error); ok {
   380  		return customErr.errorCode == NotFound && customErr.Message == KeyDoesNotExistMsg
   381  	}
   382  	return false
   383  }
   384  
   385  // IsCannotReadTenant missing godoc
   386  func IsCannotReadTenant(err error) bool {
   387  	if customErr, ok := err.(Error); ok {
   388  		return customErr.errorCode == InternalError && customErr.Message == CannotReadTenantMsg
   389  	}
   390  	return false
   391  }
   392  
   393  // IsConcurrentUpdate indicates if the provided error is thrown in case of concurrent update
   394  func IsConcurrentUpdate(err error) bool {
   395  	if customErr, ok := err.(Error); ok {
   396  		return customErr.errorCode == Unauthorized && strings.Contains(customErr.Message, UnauthorizedMsg)
   397  	}
   398  	return false
   399  }
   400  
   401  // IsNewInvalidOperationError missing godoc
   402  func IsNewInvalidOperationError(err error) bool {
   403  	return ErrorCode(err) == InvalidOperation
   404  }
   405  
   406  // IsNotFoundError missing godoc
   407  func IsNotFoundError(err error) bool {
   408  	return ErrorCode(err) == NotFound
   409  }
   410  
   411  // IsTenantRequired missing godoc
   412  func IsTenantRequired(err error) bool {
   413  	return ErrorCode(err) == TenantRequired
   414  }
   415  
   416  // IsTenantNotFoundError missing godoc
   417  func IsTenantNotFoundError(err error) bool {
   418  	return ErrorCode(err) == TenantNotFound
   419  }
   420  
   421  // IsNotUniqueError missing godoc
   422  func IsNotUniqueError(err error) bool {
   423  	return ErrorCode(err) == NotUnique
   424  }
   425  
   426  // IsNewNotNullViolationError missing godoc
   427  func IsNewNotNullViolationError(err error) bool {
   428  	return ErrorCode(err) == EmptyData
   429  }
   430  
   431  // IsNewCheckViolationError missing godoc
   432  func IsNewCheckViolationError(err error) bool {
   433  	return ErrorCode(err) == InconsistentData
   434  }
   435  
   436  // IsInvalidStatusCondition missing godoc
   437  func IsInvalidStatusCondition(err error) bool {
   438  	return ErrorCode(err) == InvalidStatusCondition
   439  }
   440  
   441  // IsCannotUpdateObjectInManyBundlesError missing godoc
   442  func IsCannotUpdateObjectInManyBundlesError(err error) bool {
   443  	return ErrorCode(err) == CannotUpdateObjectInManyBundles
   444  }
   445  
   446  // IsCannotUnassignObjectComingFromASAError missing godoc
   447  func IsCannotUnassignObjectComingFromASAError(err error) bool {
   448  	if customErr, ok := err.(Error); ok {
   449  		return customErr.errorCode == InvalidOperation && customErr.Message == CannotUnassignObjectFromASA
   450  	}
   451  	return false
   452  }
   453  
   454  func sortMapKey(m map[string]string) []string {
   455  	keys := make([]string, 0, len(m))
   456  	for k := range m {
   457  		keys = append(keys, k)
   458  	}
   459  
   460  	sort.Strings(keys)
   461  	return keys
   462  }