This is an automated email from the ASF dual-hosted git repository.

zrhoffman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 258bcfc6ac To/api errors (#7914)
258bcfc6ac is described below

commit 258bcfc6acc3be5c588f15357438d1fedb75bb50
Author: ocket8888 <[email protected]>
AuthorDate: Fri Jan 19 10:24:27 2024 -0700

    To/api errors (#7914)
    
    * Add api.Errors interface
    
    * Add usage example
---
 traffic_ops/traffic_ops_golang/api/errors.go      | 260 ++++++++++++++++++++++
 traffic_ops/traffic_ops_golang/api/errors_test.go | 176 +++++++++++++++
 2 files changed, 436 insertions(+)

diff --git a/traffic_ops/traffic_ops_golang/api/errors.go 
b/traffic_ops/traffic_ops_golang/api/errors.go
new file mode 100644
index 0000000000..22e2aef14a
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/api/errors.go
@@ -0,0 +1,260 @@
+package api
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+       "errors"
+       "fmt"
+       "net/http"
+)
+
+// Errs is the concrete implementation of Errors, which is used so that we can
+// deal with Errors everywhere without having to handle a bunch of
+// (de/)referencing everywhere.
+type Errs struct {
+       // code is the HTTP response code. If no error has occurred, this 
*should*
+       // be http.StatusOK.
+       code int
+       // systemError is an error that occurred internally; not safe for 
exposure
+       // to the user.
+       systemError error
+       // userError is an error that should be shown to the user to explain why
+       // their request failed.
+       userError error
+}
+
+// Code returns the HTTP response code. If no error has occurred, this
+// *should* return http.StatusOK.
+func (e *Errs) Code() int {
+       if e == nil {
+               return http.StatusOK
+       }
+       return e.code
+}
+
+// SetCode sets the HTTP response code returned by `Code`.
+func (e *Errs) SetCode(code int) {
+       e.code = code
+}
+
+// SystemError returns an error that occurred internally; not safe for
+// exposure to the user.
+func (e *Errs) SystemError() error {
+       if e == nil {
+               return nil
+       }
+       return e.systemError
+}
+
+// UserError returns an error that should be shown to the user to explain
+// why their request failed. This **must** return `nil` whenever `Code`
+// returns a value above 499.
+func (e *Errs) UserError() error {
+       if e == nil {
+               return nil
+       }
+       return e.userError
+}
+
+// SetSystemErrorString sets the Errs' system-level error to a new error
+// containing the passed message.
+func (e *Errs) SetSystemErrorString(err string) {
+       e.systemError = errors.New(err)
+}
+
+// SetSystemError sets the Errs' system-level error to the passed error.
+func (e *Errs) SetSystemError(err error) {
+       e.systemError = err
+}
+
+// SetSystemErrorf sets the Errs' system-level error to a new error containing
+// the passed formatted message, in the fmt package's syntax (allows wrapping
+// via %w).
+func (e *Errs) SetSystemErrorf(format string, args ...any) {
+       e.systemError = fmt.Errorf(format, args...)
+}
+
+// SetUserError sets the Errs' user-level error to the passed error.
+func (e *Errs) SetUserError(err error) {
+       e.userError = err
+}
+
+// SetUserErrorString sets the Errs' user-level error to a new error containing
+// the passed message.
+func (e *Errs) SetUserErrorString(err string) {
+       e.userError = errors.New(err)
+}
+
+// SetUserErrorf sets the Errs' user-level error to a new error containing the
+// passed formatted message, in the fmt package's syntax (allows wrapping via
+// %w).
+func (e *Errs) SetUserErrorf(format string, args ...any) {
+       e.userError = fmt.Errorf(format, args...)
+}
+
+// String implements the fmt.Stringer interface.
+func (e *Errs) String() string {
+       return fmt.Sprintf("%d %s, SystemError='%v', UserError='%v'", e.code, 
http.StatusText(e.code), e.systemError, e.userError)
+}
+
+// Error implements the error interface. This will return the user error, if
+// there is one, otherwise it will return the system error. In the case that no
+// actual error has occurred, it will neturn an empty string. This panics if 
the
+// Errs it is called on is nil (just like a normal error would).
+func (e *Errs) Error() string {
+       if e.userError != nil {
+               return e.userError.Error()
+       }
+       if e.systemError != nil {
+               return e.systemError.Error()
+       }
+       return ""
+}
+
+// Errors represents a set of zero to two errors to be handled by the API.
+type Errors interface {
+       // Code returns the HTTP response code. If no error has occurred, this
+       // *should* return http.StatusOK.
+       Code() int
+       // SetCode sets the HTTP response code returned by `Code`. In general, 
you
+       // probably won't need to use this.
+       SetCode(int)
+       // SystemError returns an error that occurred internally; not safe for
+       // exposure to the user.
+       SystemError() error
+       // SetSystemErrorString sets the Errors' system-level error to the 
passed
+       // error. In general, you probably won't need to use this.
+       SetSystemError(err error)
+       // SetSystemErrorf sets the Errors' system-level error to a new error
+       // containing the passed formatted message, in the fmt package's syntax
+       // (allows wrapping via %w). In general, you probably won't need to use
+       // this.
+       SetSystemErrorf(format string, args ...any)
+       // SetSystemErrorString sets the Errors' system-level error to a new 
error
+       // containing the passed message. In general, you probably won't need 
to use
+       // this.
+       SetSystemErrorString(err string)
+       // UserError returns an error that should be shown to the user to 
explain
+       // why their request failed. This **must** return `nil` whenever `Code`
+       // returns a value above 499.
+       UserError() error
+       // SetUserErrorString sets the Errors' user-level error to the passed 
error.
+       // In general, you probably won't need to use this.
+       SetUserError(err error)
+       // SetUserErrorf sets the Errors' user-level error to a new error 
containing
+       // the passed formatted message, in the fmt package's syntax (allows
+       // wrapping via %w). In general, you probably won't need to use this.
+       SetUserErrorf(format string, args ...any)
+       // SetUserErrorString sets the Errors' user-level error to a new error
+       // containing the passed message. In general, you probably won't need 
to use
+       // this.
+       SetUserErrorString(err string)
+
+       // String implements the fmt.Stringer interface.
+       String() string
+       // Error implements the error interface. This will return the user 
error, if
+       // there is one, otherwise it will return the system error. In the case 
that
+       // no actual error has occurred, it will neturn an empty string. This 
panics
+       // if the Errors it is called on is nil (just like a normal error 
would).
+       Error() string
+}
+
+// NewErrors directly constructs a new Errors using the passed information.
+func NewErrors(code int, userError, systemError error) Errors {
+       return &Errs{
+               code:        code,
+               systemError: systemError,
+               userError:   userError,
+       }
+}
+
+// NewSystemError creates an Errors that only contains the given system error,
+// and has the appropriate response code.
+func NewSystemError(err error) Errors {
+       return &Errs{
+               code:        http.StatusInternalServerError,
+               systemError: err,
+               userError:   nil,
+       }
+}
+
+// NewSystemErrorString creates an Errors that only contains a system error,
+// having the appropriate response code and containing the given message.
+func NewSystemErrorString(err string) Errors {
+       return &Errs{
+               code:        http.StatusInternalServerError,
+               systemError: errors.New(err),
+               userError:   nil,
+       }
+}
+
+// NewSystemErrorf creates an Errors that only contains a system error, having
+// the appropriate response code, and containing a message from the given
+// format arguments (using the fmt package's syntax, with %w allowed for
+// wrapping).
+func NewSystemErrorf(format string, args ...any) Errors {
+       return &Errs{
+               code:        http.StatusInternalServerError,
+               systemError: fmt.Errorf(format, args...),
+               userError:   nil,
+       }
+}
+
+// NewUserError creates an Errors that only contains the given user error,
+// and has the appropriate response code.
+func NewUserError(err error) Errors {
+       return &Errs{
+               code:        http.StatusInternalServerError,
+               systemError: nil,
+               userError:   err,
+       }
+}
+
+// NewUserErrorString creates an Errors that only contains a user error,
+// having the appropriate response code and containing the given message.
+func NewUserErrorString(err string) Errors {
+       return &Errs{
+               code:        http.StatusInternalServerError,
+               systemError: nil,
+               userError:   errors.New(err),
+       }
+}
+
+// NewUserErrorf creates an Errors that only contains a user error, having
+// the appropriate response code, and containing a message from the given
+// format arguments (using the fmt package's syntax, with %w allowed for
+// wrapping).
+func NewUserErrorf(format string, args ...any) Errors {
+       return &Errs{
+               code:        http.StatusInternalServerError,
+               systemError: nil,
+               userError:   fmt.Errorf(format, args...),
+       }
+}
+
+// NewResourceModifiedError creates an Errors that only contains an HTTP
+// Precondition Failed status code and associated error message.
+func NewResourceModifiedError() Errors {
+       return &Errs{
+               code:        http.StatusPreconditionFailed,
+               systemError: nil,
+               userError:   ResourceModifiedError,
+       }
+}
diff --git a/traffic_ops/traffic_ops_golang/api/errors_test.go 
b/traffic_ops/traffic_ops_golang/api/errors_test.go
new file mode 100644
index 0000000000..f2fff8a410
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/api/errors_test.go
@@ -0,0 +1,176 @@
+package api
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+       "errors"
+       "fmt"
+       "net/http"
+)
+
+func ExampleErrors_String() {
+       fmt.Println(NewErrors(http.StatusOK, nil, nil).String())
+
+       // Output: 200 OK, SystemError='<nil>', UserError='<nil>'
+}
+func ExampleErrors_Error() {
+       fmt.Println(NewErrors(http.StatusAccepted, errors.New("user error"), 
errors.New("system error")).Error())
+       // Output: user error
+}
+func ExampleErrors_Code() {
+       e := NewErrors(http.StatusOK, nil, nil)
+       fmt.Println(e.Code())
+       // Output: 200
+}
+func ExampleErrors_SetCode() {
+       e := NewErrors(http.StatusOK, errors.New("something happened"), nil)
+       e.SetCode(http.StatusBadRequest)
+       fmt.Println(e.String())
+       // Output: 400 Bad Request, SystemError='<nil>', UserError='something 
happened'
+}
+func ExampleErrors_SystemError() {
+       e := NewErrors(http.StatusAccepted, nil, errors.New("testquest"))
+       fmt.Println(e.SystemError())
+       // Output: testquest
+}
+func ExampleErrors_UserError() {
+       e := NewErrors(http.StatusAccepted, errors.New("testquest"), nil)
+       fmt.Println(e.UserError())
+       // Output: testquest
+}
+func ExampleErrors_SetSystemError() {
+       e := NewErrors(http.StatusInternalServerError, nil, nil)
+       e.SetSystemError(errors.New("testquest"))
+       fmt.Println(e.String())
+       // Output: 500 Internal Server Error, SystemError='testquest', 
UserError='<nil>'
+}
+func ExampleErrors_SetSystemErrorString() {
+       e := NewErrors(http.StatusInternalServerError, nil, nil)
+       e.SetSystemErrorString("testquest")
+       fmt.Println(e.String())
+       // Output: 500 Internal Server Error, SystemError='testquest', 
UserError='<nil>'
+}
+func ExampleErrors_SetSystemErrorf() {
+       e := NewErrors(http.StatusInternalServerError, nil, nil)
+       e.SetSystemErrorf("test: %w", errors.New("quest"))
+       fmt.Println(e.String())
+       // Output: 500 Internal Server Error, SystemError='test: quest', 
UserError='<nil>'
+}
+func ExampleErrors_SetUserError() {
+       e := NewErrors(http.StatusInternalServerError, nil, nil)
+       e.SetUserError(errors.New("testquest"))
+       fmt.Println(e.String())
+       // Output: 500 Internal Server Error, SystemError='<nil>', 
UserError='testquest'
+}
+func ExampleErrors_SetUserErrorString() {
+       e := NewErrors(http.StatusInternalServerError, nil, nil)
+       e.SetUserErrorString("testquest")
+       fmt.Println(e.String())
+       // Output: 500 Internal Server Error, SystemError='<nil>', 
UserError='testquest'
+}
+func ExampleErrors_SetUserErrorf() {
+       e := NewErrors(http.StatusInternalServerError, nil, nil)
+       e.SetUserErrorf("test: %w", errors.New("quest"))
+       fmt.Println(e.String())
+       // Output: 500 Internal Server Error, SystemError='<nil>', 
UserError='test: quest'
+}
+func ExampleErrors() {
+       handler := func(fail bool) Errors {
+               if fail {
+                       return NewSystemErrorString("failed")
+               }
+               return nil
+       }
+
+       var errs error = handler(true)
+       fmt.Println(errs)
+
+       errs = handler(false)
+       fmt.Println(errs)
+
+       // Output: failed
+       // <nil>
+}
+
+func ExampleErrs_Code() {
+       var e *Errs
+       fmt.Println(e.Code())
+       // Output: 200
+}
+func ExampleErrs_SystemError() {
+       var e *Errs
+       fmt.Println(e.SystemError())
+       // Output: <nil>
+}
+func ExampleErrs_UserError() {
+       var e *Errs
+       fmt.Println(e.UserError())
+       // Output: <nil>
+}
+func ExampleErrs_Error() {
+       e := &Errs{}
+       fmt.Println(e.Error())
+
+       e.SetSystemError(errors.New("testquest"))
+       fmt.Println(e.Error())
+
+       // Output:
+       // testquest
+}
+
+func ExamlpleNewErrors() {
+       fmt.Println(
+               NewErrors(
+                       http.StatusForbidden,
+                       errors.New("you don't have permission to do that in 
that Tenant"),
+                       errors.New("user tried to access a forbidden tenant"),
+               ),
+       )
+
+       // Output: 403 Forbidden, SystemError='user tried to access a forbidden 
tenant', UserError='you don't have permission to do that in that Tenant'
+}
+func ExampleNewSystemError() {
+       fmt.Println(NewSystemError(errors.New("testquest")).String())
+       // Output: 500 Internal Server Error, SystemError='testquest', 
UserError='<nil>'
+}
+func ExampleNewSystemErrorString() {
+       fmt.Println(NewSystemErrorString("testquest").String())
+       // Output: 500 Internal Server Error, SystemError='testquest', 
UserError='<nil>'
+}
+func ExampleNewSystemErrorf() {
+       fmt.Println(NewSystemErrorf("test: %w", errors.New("quest")).String())
+       // Output: 500 Internal Server Error, SystemError='test: quest', 
UserError='<nil>'
+}
+func ExampleNewUserError() {
+       fmt.Println(NewUserError(errors.New("testquest")).String())
+       // Output: 500 Internal Server Error, SystemError='<nil>', 
UserError='testquest'
+}
+func ExampleNewUserErrorString() {
+       fmt.Println(NewUserErrorString("testquest").String())
+       // Output: 500 Internal Server Error, SystemError='<nil>', 
UserError='testquest'
+}
+func ExampleNewUserErrorf() {
+       fmt.Println(NewUserErrorf("test: %w", errors.New("quest")).String())
+       // Output: 500 Internal Server Error, SystemError='<nil>', 
UserError='test: quest'
+}
+func ExampleNewResourceModifiedError() {
+       fmt.Println(NewResourceModifiedError().String())
+       // Output: 412 Precondition Failed, SystemError='<nil>', 
UserError='resource was modified since the time specified by the request 
headers'
+}

Reply via email to