Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <psteinhardt@gitlab.com>2023-07-06 09:06:10 +0300
committerPatrick Steinhardt <psteinhardt@gitlab.com>2023-07-06 14:00:01 +0300
commit9f7b469b4c2944c0bb057a79e9e2c28aa2d283e1 (patch)
tree4e9d31f8fbd9e460d5085b26f5414c7f172b7abd
parent23c5e74210aac0f360bb24cb2677bf37c559322f (diff)
structerr: Allow addding metadata to any error type
In order to add metadata to an error one must create a `structerr.Error` right now. This is a bit roundabout though in the case where you already have a custom error type that you wish to add metadata to. While you can play games and implement an `Unwrap()` function on your error type that returns a `structerr.Error` with metadata, this is indeed quite tedious and roundabout. Instead, provide a new `structerr.ErrorMetadater` interface that can be implemented by custom error types. This allows them to attach metadata to the custom error type directly via a `ErrorMetadata()` function.
-rw-r--r--internal/structerr/error.go29
-rw-r--r--internal/structerr/error_test.go24
2 files changed, 46 insertions, 7 deletions
diff --git a/internal/structerr/error.go b/internal/structerr/error.go
index 786522516..048258b6b 100644
--- a/internal/structerr/error.go
+++ b/internal/structerr/error.go
@@ -342,8 +342,16 @@ func (e Error) WithGRPCCode(code codes.Code) Error {
return e
}
-// ExtractMetadata extracts metadata from the given error if any of the errors in its chain contain any. The metadata
-// will contain the combination of all added metadata of this error as well as any wrapped Errors.
+// ErrorMetadater is an interface that can be implemented by error types in order to provide custom metadata items
+// without itself being a `structerr.Error`.
+type ErrorMetadater interface {
+ // ErrorMetadata returns the list of metadata items attached to this error.
+ ErrorMetadata() []MetadataItem
+}
+
+// ExtractMetadata extracts metadata from the given error if any of the errors in its chain contain any. Errors may
+// contain in case they are either a `structerr.Error` or in case they implement the `ErrorMetadater` interface. The
+// metadata will contain the combination of all added metadata of this error as well as any wrapped Errors.
//
// When the same metada key exists multiple times in the error chain, then the value that is
// highest up the callchain will be returned. This is done because in general, the higher up the
@@ -352,11 +360,18 @@ func ExtractMetadata(err error) map[string]any {
metadata := map[string]any{}
for ; err != nil; err = errors.Unwrap(err) {
- if structErr, ok := err.(Error); ok {
- for _, m := range structErr.metadata {
- if _, exists := metadata[m.Key]; !exists {
- metadata[m.Key] = m.Value
- }
+ var metadataItems []MetadataItem
+ if structerr, ok := err.(Error); ok {
+ metadataItems = structerr.metadata
+ } else if errorMetadater, ok := err.(ErrorMetadater); ok {
+ metadataItems = errorMetadater.ErrorMetadata()
+ } else {
+ continue
+ }
+
+ for _, m := range metadataItems {
+ if _, exists := metadata[m.Key]; !exists {
+ metadata[m.Key] = m.Value
}
}
}
diff --git a/internal/structerr/error_test.go b/internal/structerr/error_test.go
index dc8852977..a998e5329 100644
--- a/internal/structerr/error_test.go
+++ b/internal/structerr/error_test.go
@@ -325,6 +325,18 @@ func TestError_Is(t *testing.T) {
}
}
+type customWithMetadataError struct{}
+
+func (customWithMetadataError) Error() string {
+ return "custom error"
+}
+
+func (customWithMetadataError) ErrorMetadata() []MetadataItem {
+ return []MetadataItem{
+ {Key: "custom", Value: "error"},
+ }
+}
+
func TestError_Metadata(t *testing.T) {
t.Parallel()
@@ -479,6 +491,18 @@ func TestError_Metadata(t *testing.T) {
{Key: "b", Value: "b"},
})
})
+
+ t.Run("custom type with metadata", func(t *testing.T) {
+ err := New("top-level: %w", customWithMetadataError{})
+
+ require.Equal(t, Error{
+ err: fmt.Errorf("top-level: %w", customWithMetadataError{}),
+ code: codes.Unknown,
+ }, err)
+ requireItems(t, err, []MetadataItem{
+ {Key: "custom", Value: "error"},
+ })
+ })
}
func TestError_Details(t *testing.T) {