diff --git a/cmd/cone/main.go b/cmd/cone/main.go index 6e6dc75e..fcaebd7d 100644 --- a/cmd/cone/main.go +++ b/cmd/cone/main.go @@ -7,7 +7,7 @@ import ( "os/signal" "syscall" - "github.com/conductorone/cone/pkg/output" + "github.com/conductorone/cone/pkg/client" "github.com/spf13/cobra" ) @@ -74,7 +74,7 @@ func runCli(ctx context.Context) int { err = cliCmd.ExecuteContext(ctx) if err != nil { _, _, v, _ := cmdContext(cliCmd) - fmt.Fprintln(os.Stderr, output.HandleErrors(ctx, v, err)) + fmt.Fprintln(os.Stderr, client.HandleErrors(ctx, v, err)) return 1 } diff --git a/pkg/client/app.go b/pkg/client/app.go index 2945b725..87e508ab 100644 --- a/pkg/client/app.go +++ b/pkg/client/app.go @@ -15,7 +15,7 @@ func (c *client) GetApp(ctx context.Context, appID string) (*shared.App, error) return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } return resp.GetAppResponse.App, nil @@ -33,7 +33,7 @@ func (c *client) ListApps(ctx context.Context) ([]shared.App, error) { if err != nil { return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } diff --git a/pkg/client/client.go b/pkg/client/client.go index 43ae9c46..b08055c5 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -2,8 +2,6 @@ package client import ( "context" - "fmt" - "io" "net/http" "net/url" @@ -131,18 +129,3 @@ func New( return c, nil } - -func handleBadStatus(resp *http.Response) error { - // This is added temporarily to ensure we return an error if we get a non-success status code. - // Eventually (ideally), we'll be generating this error handling as part of the SDK - if resp.StatusCode >= http.StatusBadRequest { - body, err := io.ReadAll(resp.Body) - if err != nil { - return err - } - - return fmt.Errorf("status %d: %s", resp.StatusCode, string(body)) - } - - return nil -} diff --git a/pkg/client/entitlement.go b/pkg/client/entitlement.go index c490e32e..225227b1 100644 --- a/pkg/client/entitlement.go +++ b/pkg/client/entitlement.go @@ -114,7 +114,7 @@ func (c *client) SearchEntitlements(ctx context.Context, filter *SearchEntitleme return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } @@ -174,8 +174,7 @@ func (c *client) GetEntitlement(ctx context.Context, appId string, entitlementId if err != nil { return nil, err } - - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } @@ -203,7 +202,7 @@ func (c *client) ListEntitlements(ctx context.Context, appId string) ([]shared.A if err != nil { return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } diff --git a/pkg/client/error.go b/pkg/client/error.go new file mode 100644 index 00000000..0521cf59 --- /dev/null +++ b/pkg/client/error.go @@ -0,0 +1,73 @@ +package client + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + + "github.com/conductorone/cone/pkg/output" + "github.com/spf13/viper" +) + +const defaultJSONError = `{"error": "unable to marshal error to JSON %s"}` + +type JSONError struct { + Error string `json:"error"` +} + +type HTTPError struct { + StatusCode int `json:"status_code"` + Body string `json:"body"` +} + +func NewHTTPError(resp *http.Response) *HTTPError { + // This is added temporarily to ensure we return an error if we get a non-success status code. + // Eventually (ideally), we'll be generating this error handling as part of the SDK + if resp.StatusCode >= http.StatusBadRequest { + var httpErr HTTPError + body, err := io.ReadAll(resp.Body) + if err != nil { + httpErr = HTTPError{ + StatusCode: resp.StatusCode, + Body: fmt.Errorf("unable to read response body: %w", err).Error(), + } + } else { + httpErr = HTTPError{ + StatusCode: resp.StatusCode, + Body: string(body), + } + } + return &httpErr + } + + return nil +} + +func (e *HTTPError) Error() string { + return fmt.Sprintf("%d: %s", e.StatusCode, e.Body) +} + +func HandleErrors(ctx context.Context, v *viper.Viper, input error) error { + outputType := v.GetString("output") + if outputType != "json" && outputType != output.JSONPretty { + return input + } + var jsonError []byte + + var httpErr *HTTPError + if errors.As(input, &httpErr) { + jsonError, err := output.MakeJSONFromInterface(ctx, httpErr, outputType == output.JSONPretty) + if err != nil { + return fmt.Errorf(defaultJSONError, httpErr.Error()) + } + return fmt.Errorf(string(jsonError)) + } + jsonError, err := output.MakeJSONFromInterface(ctx, JSONError{Error: input.Error()}, outputType == output.JSONPretty) + if err != nil { + return fmt.Errorf(defaultJSONError, input.Error()) + } + + return fmt.Errorf(string(jsonError)) +} diff --git a/pkg/client/grant.go b/pkg/client/grant.go index e007907c..75151886 100644 --- a/pkg/client/grant.go +++ b/pkg/client/grant.go @@ -17,7 +17,7 @@ func (c *client) GetGrantsForIdentity(ctx context.Context, appID string, appEnti return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } diff --git a/pkg/client/policy.go b/pkg/client/policy.go index 43a2e00b..6a656e1e 100644 --- a/pkg/client/policy.go +++ b/pkg/client/policy.go @@ -19,7 +19,7 @@ func (c *client) ListPolicies(ctx context.Context) ([]shared.Policy, error) { if err != nil { return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } diff --git a/pkg/client/resource_type.go b/pkg/client/resource_type.go index c7d3336a..c3094047 100644 --- a/pkg/client/resource_type.go +++ b/pkg/client/resource_type.go @@ -17,7 +17,7 @@ func (c *client) GetResourceType(ctx context.Context, appID string, resourceType return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } v := resp.AppResourceTypeServiceGetResponse.AppResourceTypeView @@ -43,7 +43,7 @@ func (c *client) GetResource(ctx context.Context, appID string, resourceTypeID s return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } v := resp.AppResourceServiceGetResponse.AppResourceView diff --git a/pkg/client/task.go b/pkg/client/task.go index 83349000..19ecf6e0 100644 --- a/pkg/client/task.go +++ b/pkg/client/task.go @@ -13,7 +13,7 @@ func (c *client) GetTask(ctx context.Context, taskId string) (*shared.TaskServic return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } @@ -44,7 +44,7 @@ func (c *client) CreateGrantTask( return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } @@ -69,7 +69,7 @@ func (c *client) CreateRevokeTask( return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } @@ -82,7 +82,7 @@ func (c *client) SearchTasks(ctx context.Context, taskFilter shared.TaskSearchRe return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } @@ -100,7 +100,7 @@ func (c *client) CommentOnTask(ctx context.Context, taskID string, comment strin return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } return resp.TaskActionsServiceCommentResponse, nil @@ -118,7 +118,7 @@ func (c *client) ApproveTask(ctx context.Context, taskId string, comment string, return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } return resp.TaskActionsServiceApproveResponse, nil @@ -136,7 +136,7 @@ func (c *client) DenyTask(ctx context.Context, taskId string, comment string, po return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } return resp.TaskActionsServiceDenyResponse, nil @@ -151,7 +151,7 @@ func (c *client) EscalateTask(ctx context.Context, taskID string) (*shared.TaskS return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } return resp.TaskServiceActionResponse, nil diff --git a/pkg/client/user.go b/pkg/client/user.go index 04aa6469..a57a8e79 100644 --- a/pkg/client/user.go +++ b/pkg/client/user.go @@ -14,7 +14,7 @@ func (c *client) GetUser(ctx context.Context, userID string) (*shared.User, erro return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } diff --git a/pkg/client/whoami.go b/pkg/client/whoami.go index 71f4bdef..40a27d18 100644 --- a/pkg/client/whoami.go +++ b/pkg/client/whoami.go @@ -12,7 +12,7 @@ func (c *client) AuthIntrospect(ctx context.Context) (*shared.IntrospectResponse return nil, err } - if err := handleBadStatus(resp.RawResponse); err != nil { + if err := NewHTTPError(resp.RawResponse); err != nil { return nil, err } diff --git a/pkg/output/error.go b/pkg/output/error.go deleted file mode 100644 index d48c04ba..00000000 --- a/pkg/output/error.go +++ /dev/null @@ -1,28 +0,0 @@ -package output - -import ( - "context" - "fmt" - - "github.com/spf13/viper" -) - -const defaultJSONError = `{"error": "unable to marshal error to JSON %s"}` - -type JSONError struct { - Error string `json:"error"` -} - -func HandleErrors(ctx context.Context, v *viper.Viper, input error) error { - outputType := v.GetString("output") - if outputType != "json" && outputType != JSONPretty { - return input - } - // TODO: @anthony - handle errors better, for example, HTTP errors could be better, see client.go - jsonError, err := MakeJSONFromInterface(ctx, JSONError{Error: input.Error()}, outputType == JSONPretty) - if err != nil { - return fmt.Errorf(defaultJSONError, input.Error()) - } - - return fmt.Errorf(string(jsonError)) -}