@@ -3,12 +3,14 @@ package client
33import (
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.
4241func 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
5199func 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"}
116164func 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
0 commit comments