Repository: labstack/echo
Pull request #2340 (2022-11-19 19:24:43)

Add new method HTTPError.WithInternal
Add new method HTTPError.WithInternal to be able to create clone of HTTPError with given error set as internal

Currently HTTPError has method `SetInternal` but this mutates that same error which is problematic when we use those public errors we have ala `echo.ErrUnauthorized` etc. For these cases creating clone of that error instance is better. 

codecov[bot]: # [Codecov](https://codecov.io/gh/labstack/echo/pull/2340?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack) Report
Base: **92.37**% // Head: **92.38**% // Increases project coverage by **`+0.01%`** :tada:
> Coverage data is based on head [(`36c3eaf`)](https://codecov.io/gh/labstack/echo/pull/2340?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack) compared to base [(`fd2b102`)](https://codecov.io/gh/labstack/echo/commit/fd2b102d3ee1bd538e13418f6382e98cff77fdb0?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack).
> Patch coverage: 100.00% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #2340      +/-   ##
==========================================
+ Coverage   92.37%   92.38%   +0.01%     
==========================================
  Files          37       37              
  Lines        4448     4454       +6     
==========================================
+ Hits         4109     4115       +6     
  Misses        247      247              
  Partials       92       92              
```


| [Impacted Files](https://codecov.io/gh/labstack/echo/pull/2340?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack) | Coverage Δ | |
|---|---|---|
| [echo.go](https://codecov.io/gh/labstack/echo/pull/2340/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack#diff-ZWNoby5nbw==) | `95.24% <100.00%> (+0.06%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/labstack/echo/pull/2340?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=labstack).


Patch:
diff --git a/echo.go b/echo.go
index 068558a..28f6565 100644
--- a/echo.go
+++ b/echo.go
@@ -3,34 +3,34 @@ Package echo implements high performance, minimalist Go web framework.
 
 Example:
 
-  package main
+	package main
 
-  import (
-    "net/http"
+	import (
+	  "net/http"
 
-    "github.com/labstack/echo/v4"
-    "github.com/labstack/echo/v4/middleware"
-  )
+	  "github.com/labstack/echo/v4"
+	  "github.com/labstack/echo/v4/middleware"
+	)
 
-  // Handler
-  func hello(c echo.Context) error {
-    return c.String(http.StatusOK, "Hello, World!")
-  }
+	// Handler
+	func hello(c echo.Context) error {
+	  return c.String(http.StatusOK, "Hello, World!")
+	}
 
-  func main() {
-    // Echo instance
-    e := echo.New()
+	func main() {
+	  // Echo instance
+	  e := echo.New()
 
-    // Middleware
-    e.Use(middleware.Logger())
-    e.Use(middleware.Recover())
+	  // Middleware
+	  e.Use(middleware.Logger())
+	  e.Use(middleware.Recover())
 
-    // Routes
-    e.GET("/", hello)
+	  // Routes
+	  e.GET("/", hello)
 
-    // Start server
-    e.Logger.Fatal(e.Start(":1323"))
-  }
+	  // Start server
+	  e.Logger.Fatal(e.Start(":1323"))
+	}
 
 Learn more at https://echo.labstack.com
 */
@@ -884,6 +884,15 @@ func (he *HTTPError) SetInternal(err error) *HTTPError {
 	return he
 }
 
+// WithInternal returns clone of HTTPError with err set to HTTPError.Internal field
+func (he *HTTPError) WithInternal(err error) *HTTPError {
+	return &HTTPError{
+		Code:     he.Code,
+		Message:  he.Message,
+		Internal: err,
+	}
+}
+
 // Unwrap satisfies the Go 1.13 error wrapper interface.
 func (he *HTTPError) Unwrap() error {
 	return he.Internal
@@ -913,8 +922,8 @@ func WrapMiddleware(m func(http.Handler) http.Handler) MiddlewareFunc {
 
 // GetPath returns RawPath, if it's empty returns Path from URL
 // Difference between RawPath and Path is:
-//  * Path is where request path is stored. Value is stored in decoded form: /%47%6f%2f becomes /Go/.
-//  * RawPath is an optional field which only gets set if the default encoding is different from Path.
+//   - Path is where request path is stored. Value is stored in decoded form: /%47%6f%2f becomes /Go/.
+//   - RawPath is an optional field which only gets set if the default encoding is different from Path.
 func GetPath(r *http.Request) string {
 	path := r.URL.RawPath
 	if path == "" {


Test patch:
diff --git a/echo_test.go b/echo_test.go
index 6bece4f..aa2954c 100644
--- a/echo_test.go
+++ b/echo_test.go
@@ -1206,13 +1206,22 @@ func TestHTTPError(t *testing.T) {
 
 		assert.Equal(t, "code=400, message=map[code:12]", err.Error())
 	})
-	t.Run("internal", func(t *testing.T) {
+
+	t.Run("internal and SetInternal", func(t *testing.T) {
 		err := NewHTTPError(http.StatusBadRequest, map[string]interface{}{
 			"code": 12,
 		})
 		err.SetInternal(errors.New("internal error"))
 		assert.Equal(t, "code=400, message=map[code:12], internal=internal error", err.Error())
 	})
+
+	t.Run("internal and WithInternal", func(t *testing.T) {
+		err := NewHTTPError(http.StatusBadRequest, map[string]interface{}{
+			"code": 12,
+		})
+		err = err.WithInternal(errors.New("internal error"))
+		assert.Equal(t, "code=400, message=map[code:12], internal=internal error", err.Error())
+	})
 }
 
 func TestHTTPError_Unwrap(t *testing.T) {
@@ -1223,13 +1232,22 @@ func TestHTTPError_Unwrap(t *testing.T) {
 
 		assert.Nil(t, errors.Unwrap(err))
 	})
-	t.Run("internal", func(t *testing.T) {
+
+	t.Run("unwrap internal and SetInternal", func(t *testing.T) {
 		err := NewHTTPError(http.StatusBadRequest, map[string]interface{}{
 			"code": 12,
 		})
 		err.SetInternal(errors.New("internal error"))
 		assert.Equal(t, "internal error", errors.Unwrap(err).Error())
 	})
+
+	t.Run("unwrap internal and WithInternal", func(t *testing.T) {
+		err := NewHTTPError(http.StatusBadRequest, map[string]interface{}{
+			"code": 12,
+		})
+		err = err.WithInternal(errors.New("internal error"))
+		assert.Equal(t, "internal error", errors.Unwrap(err).Error())
+	})
 }
 
 func TestDefaultHTTPErrorHandler(t *testing.T) {