Skip to content

Commit c801c69

Browse files
authored
Fix missing "caused by" information in StructError (#752)
Signed-off-by: Carlos Garcia <carlos.garcia@mattermost.com>
1 parent e31c031 commit c801c69

3 files changed

Lines changed: 69 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
2828
### Removed
2929

3030
### Fixed
31+
- Missing "caused by" information in StructError ([#752](https://github.com/opensearch-project/opensearch-go/pull/752))
3132

3233
### Security
3334

error.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,26 @@ type Err struct {
7575
RootCause []RootCause `json:"root_cause"`
7676
Type string `json:"type"`
7777
Reason string `json:"reason"`
78+
CausedBy *CausedBy `json:"caused_by,omitempty"`
7879
Index string `json:"index,omitempty"`
7980
IndexUUID string `json:"index_uuid,omitempty"`
8081
}
8182

83+
// CausedBy represents the optional caused_by of an API error response. Causes can be nested
84+
type CausedBy struct {
85+
Type string `json:"type"`
86+
Reason string `json:"reason"`
87+
CausedBy *CausedBy `json:"caused_by,omitempty"`
88+
}
89+
90+
// String returns a string representation of CausedBy, handling nested structures
91+
func (c *CausedBy) String() string {
92+
if c.CausedBy == nil {
93+
return fmt.Sprintf("{type: %s, reason: %s}", c.Type, c.Reason)
94+
}
95+
return fmt.Sprintf("{type: %s, reason: %s, caused_by: %s}", c.Type, c.Reason, c.CausedBy)
96+
}
97+
8298
// RootCause represents the root_cause of an API error response
8399
type RootCause struct {
84100
Type string `json:"type"`
@@ -89,7 +105,13 @@ type RootCause struct {
89105

90106
// Error returns a string
91107
func (e StructError) Error() string {
92-
return fmt.Sprintf("status: %d, type: %s, reason: %s, root_cause: %s", e.Status, e.Err.Type, e.Err.Reason, e.Err.RootCause)
108+
result := fmt.Sprintf("status: %d, type: %s, reason: %s, root_cause: %s", e.Status, e.Err.Type, e.Err.Reason, e.Err.RootCause)
109+
110+
if e.Err.CausedBy != nil {
111+
result += fmt.Sprintf(", caused_by: %s", e.Err.CausedBy)
112+
}
113+
114+
return result
93115
}
94116

95117
// UnmarshalJSON is a custom unmarshal function for StructError returning custom errors in special cases

error_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,51 @@ func TestError(t *testing.T) {
6464
_ = fmt.Sprintf("%s", err)
6565
})
6666

67+
t.Run("CausedBy", func(t *testing.T) {
68+
resp := &opensearch.Response{
69+
StatusCode: http.StatusBadRequest,
70+
Body: io.NopCloser(
71+
strings.NewReader(`{
72+
"error":{
73+
"root_cause":[{
74+
"type":"illegal_argument_exception",
75+
"reason":"composable template [posts] template after composition is invalid"
76+
}],
77+
"type":"illegal_argument_exception",
78+
"reason":"composable template [posts] template after composition is invalid",
79+
"caused_by":{
80+
"type":"illegal_argument_exception",
81+
"reason":"Custom analyzer [mm_analyzer] failed to find filter under name [test_filter]",
82+
"caused_by":{
83+
"type":"illegal_argument_exception",
84+
"reason":"test caused by"
85+
}
86+
}
87+
},
88+
"status":400
89+
}`),
90+
),
91+
}
92+
assert.True(t, resp.IsError())
93+
err := opensearch.ParseError(resp)
94+
var testError *opensearch.StructError
95+
require.True(t, errors.As(err, &testError))
96+
assert.Equal(t, http.StatusBadRequest, testError.Status)
97+
assert.Equal(t, "illegal_argument_exception", testError.Err.Type)
98+
assert.Equal(t, "composable template [posts] template after composition is invalid", testError.Err.Reason)
99+
assert.NotNil(t, testError.Err.RootCause)
100+
assert.Equal(t, "illegal_argument_exception", testError.Err.RootCause[0].Type)
101+
assert.Equal(t, "composable template [posts] template after composition is invalid", testError.Err.RootCause[0].Reason)
102+
assert.NotNil(t, testError.Err.CausedBy)
103+
assert.Equal(t, "illegal_argument_exception", testError.Err.CausedBy.Type)
104+
assert.Equal(t, "Custom analyzer [mm_analyzer] failed to find filter under name [test_filter]", testError.Err.CausedBy.Reason)
105+
assert.NotNil(t, testError.Err.CausedBy.CausedBy)
106+
assert.Equal(t, "illegal_argument_exception", testError.Err.CausedBy.CausedBy.Type)
107+
assert.Equal(t, "test caused by", testError.Err.CausedBy.CausedBy.Reason)
108+
assert.Nil(t, testError.Err.CausedBy.CausedBy.CausedBy)
109+
_ = fmt.Sprintf("%s", err)
110+
})
111+
67112
t.Run("Unmarshal errors", func(t *testing.T) {
68113
t.Run("dummy", func(t *testing.T) {
69114
reader := io.NopCloser(

0 commit comments

Comments
 (0)