Skip to content

Commit 735b03f

Browse files
authored
[BB-1891] fix error wrapping for slack-go library errors (#68)
* fix error wrapping for slack-go library errors * remove contextmsg inline creation * fix errors.As, needs to be pointer to pointer * update code for badrequest
1 parent fb7429d commit 735b03f

File tree

2 files changed

+67
-18
lines changed

2 files changed

+67
-18
lines changed

pkg/connector/client/helpers.go

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ package client
33
import (
44
"context"
55
"encoding/json"
6+
"errors"
67
"fmt"
78
"net/http"
89
"strings"
910

1011
"github.com/conductorone/baton-sdk/pkg/uhttp"
1112
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
13+
"github.com/slack-go/slack"
1214
"go.uber.org/zap"
1315
"google.golang.org/grpc/codes"
1416
)
@@ -35,17 +37,63 @@ func logBody(ctx context.Context, response *http.Response) {
3537
l.Info("response body", zap.String("body", string(body)))
3638
}
3739

38-
// Slack API may return errors in the response body even when the HTTP status code is 200.
39-
//
40-
// extracts a Slack error from a Go error and wraps it with the appropriate gRPC code.
41-
// This is useful when working with the slack-go library which returns plain Go errors.
40+
// Inspects the error returned by the slack-go library and maps it to appropriate gRPC codes.
4241
func WrapError(err error, contextMsg string) error {
4342
if err == nil {
4443
return nil
4544
}
4645

47-
grpcCode := MapSlackErrorToGRPCCode(err.Error())
48-
return uhttp.WrapErrors(grpcCode, contextMsg, err)
46+
// for rate limit errors
47+
var slackLibrateLimitErr *slack.RateLimitedError
48+
if errors.As(err, &slackLibrateLimitErr) {
49+
return uhttp.WrapErrors(codes.Unavailable, contextMsg, err)
50+
}
51+
52+
// for 5xx status codes
53+
var slackLibErr *slack.StatusCodeError
54+
if errors.As(err, &slackLibErr) {
55+
grpcCode := httpStatusToGRPCCode(slackLibErr.Code)
56+
contextMsg = fmt.Sprintf("Slack-go API HTTP error: %s : %s", slackLibErr.Status, contextMsg)
57+
return uhttp.WrapErrors(grpcCode, contextMsg, err)
58+
}
59+
60+
// when ok: false even with 200 HTTP status codes
61+
var slackErrResp *slack.SlackErrorResponse
62+
if errors.As(err, &slackErrResp) {
63+
grpcCode := MapSlackErrorToGRPCCode(slackErrResp.Err)
64+
if len(slackErrResp.ResponseMetadata.Messages) > 0 {
65+
contextMsg = fmt.Sprintf("%s (details: %v)", contextMsg, slackErrResp.ResponseMetadata.Messages)
66+
}
67+
if len(slackErrResp.ResponseMetadata.Warnings) > 0 {
68+
contextMsg = fmt.Sprintf("%s (warnings: %v)", contextMsg, slackErrResp.ResponseMetadata.Warnings)
69+
}
70+
return uhttp.WrapErrors(grpcCode, contextMsg, err)
71+
}
72+
73+
return uhttp.WrapErrors(codes.Unknown, contextMsg, err)
74+
}
75+
76+
func httpStatusToGRPCCode(httpStatus int) codes.Code {
77+
switch httpStatus {
78+
case http.StatusBadRequest:
79+
return codes.Internal
80+
case http.StatusUnauthorized:
81+
return codes.Unauthenticated
82+
case http.StatusForbidden:
83+
return codes.PermissionDenied
84+
case http.StatusNotFound:
85+
return codes.NotFound
86+
case http.StatusConflict:
87+
return codes.AlreadyExists
88+
case http.StatusTooManyRequests:
89+
return codes.Unavailable
90+
case http.StatusInternalServerError:
91+
return codes.Internal
92+
case http.StatusServiceUnavailable:
93+
return codes.Unavailable
94+
default:
95+
return codes.Unknown
96+
}
4997
}
5098

5199
func containsAny(s string, substrs ...string) bool {
@@ -74,7 +122,7 @@ func MapSlackErrorToGRPCCode(slackError string) codes.Code {
74122
return codes.Unavailable
75123
}
76124

77-
if containsAny(err, "user_not_found") {
125+
if containsAny(err, "user_not_found", "user_already_deleted") {
78126
return codes.NotFound
79127
}
80128

@@ -88,7 +136,7 @@ func MapSlackErrorToGRPCCode(slackError string) codes.Code {
88136
return codes.InvalidArgument
89137
}
90138

91-
if containsAny(err, "user_already_deleted", "two_factor_setup_required") {
139+
if containsAny(err, "two_factor_setup_required") {
92140
return codes.FailedPrecondition
93141
}
94142

@@ -114,21 +162,17 @@ func MapSlackErrorToGRPCCode(slackError string) codes.Code {
114162
// {"ok":false,"error":"invalid_auth"}
115163
// {"ok":false,"error": "user_not_found"}
116164
func ErrorWithGrpcCodeFromBytes(bodyBytes []byte) error {
117-
var baseCheck struct {
118-
Ok bool `json:"ok"`
119-
Error string `json:"error"`
120-
}
121-
122-
if err := json.Unmarshal(bodyBytes, &baseCheck); err != nil {
165+
var res SlackErrorResponse
166+
if err := json.Unmarshal(bodyBytes, &res); err != nil {
123167
return fmt.Errorf("error parsing Slack API response: %w", err)
124168
}
125169

126-
if !baseCheck.Ok && baseCheck.Error != "" {
127-
grpcCode := MapSlackErrorToGRPCCode(baseCheck.Error)
170+
if !res.Ok && res.Error != "" {
171+
grpcCode := MapSlackErrorToGRPCCode(res.Error)
128172
return uhttp.WrapErrors(
129173
grpcCode,
130-
fmt.Sprintf("Slack API error: %s", baseCheck.Error),
131-
fmt.Errorf("slack error: %s", baseCheck.Error),
174+
fmt.Sprintf("Slack API error: %s", res.Error),
175+
fmt.Errorf("slack error: %s", res.Error),
132176
)
133177
}
134178

pkg/connector/client/models.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ type BaseResponse struct {
99
Provided string `json:"provided"`
1010
}
1111

12+
type SlackErrorResponse struct {
13+
Ok bool `json:"ok"`
14+
Error string `json:"error"`
15+
}
16+
1217
type Pagination struct {
1318
ResponseMetadata struct {
1419
NextCursor string `json:"next_cursor"`

0 commit comments

Comments
 (0)