Skip to content

Commit

Permalink
Fix Inconsistent Error Handling in ReadUser (#642)
Browse files Browse the repository at this point in the history
  • Loading branch information
bobbyiliev authored Aug 29, 2024
1 parent 4e2721a commit a4371ec
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 31 deletions.
53 changes: 49 additions & 4 deletions pkg/clients/frontegg_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,10 @@ func (c *FronteggClient) RefreshToken() error {

// Helper function to handle API errors
func HandleApiError(resp *http.Response) error {
responseBody, _ := io.ReadAll(resp.Body)
if resp.StatusCode == http.StatusNotFound {
return nil
return NewFronteggAPIError(resp, "Resource not found")
}
return fmt.Errorf("API error: %s - %s", resp.Status, string(responseBody))
return HandleAPIError(resp)
}

// Helper function to perform HTTP requests
Expand All @@ -261,5 +260,51 @@ func FronteggRequest(ctx context.Context, client *FronteggClient, method, url st
}
req.Header.Add("Content-Type", "application/json")

return client.HTTPClient.Do(req)
resp, err := client.HTTPClient.Do(req)
if err != nil {
return nil, err
}

if resp.StatusCode >= 400 {
return nil, HandleApiError(resp)
}

return resp, nil
}

// FronteggAPIError represents a standardized error structure for Frontegg API calls
type FronteggAPIError struct {
StatusCode int
Message string
}

func (e *FronteggAPIError) Error() string {
return fmt.Sprintf("Frontegg API error (HTTP %d): %s", e.StatusCode, e.Message)
}

// NewFronteggAPIError creates a new FronteggAPIError instance
func NewFronteggAPIError(resp *http.Response, message string) *FronteggAPIError {
return &FronteggAPIError{
StatusCode: resp.StatusCode,
Message: message,
}
}

// HandleAPIError processes an HTTP response and returns a FronteggAPIError if applicable
func HandleAPIError(resp *http.Response) error {
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
return nil
}

responseBody, _ := io.ReadAll(resp.Body)
message := fmt.Sprintf("unexpected status code: %d - %s", resp.StatusCode, string(responseBody))
return NewFronteggAPIError(resp, message)
}

// IsNotFoundError checks if the error is a 404 Not Found error
func IsNotFoundError(err error) bool {
if fronteggErr, ok := err.(*FronteggAPIError); ok {
return fronteggErr.StatusCode == http.StatusNotFound
}
return false
}
25 changes: 4 additions & 21 deletions pkg/frontegg/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,24 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"

"github.com/MaterializeInc/terraform-provider-materialize/pkg/clients"
)

func doRequest(ctx context.Context, client *clients.FronteggClient, method, endpoint string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequestWithContext(ctx, method, endpoint, body)
if err != nil {
return nil, err
}
var bodyBytes []byte
var err error

req.Header.Add("Authorization", "Bearer "+client.Token)
if body != nil {
req.Header.Add("Content-Type", "application/json")
}

resp, err := client.HTTPClient.Do(req)
if err != nil {
return nil, err
}

if resp.StatusCode >= 400 {
var sb strings.Builder
_, err = io.Copy(&sb, resp.Body)
resp.Body.Close()
bodyBytes, err = io.ReadAll(body)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("HTTP request error: status %d, response: %s", resp.StatusCode, sb.String())
}

return resp, nil
return clients.FronteggRequest(ctx, client, method, endpoint, bodyBytes)
}

func jsonEncode(payload interface{}) (*bytes.Buffer, error) {
Expand Down
6 changes: 1 addition & 5 deletions pkg/frontegg/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,13 @@ func CreateUser(ctx context.Context, client *clients.FronteggClient, userRequest
func ReadUser(ctx context.Context, client *clients.FronteggClient, userID string) (UserResponse, error) {
var userResponse UserResponse

endpoint := fmt.Sprintf("%s%s/%s", client.Endpoint, UsersApiPathV1, userID)
endpoint := fmt.Sprintf("%s%s/%s", client.GetEndpoint(), UsersApiPathV1, userID)
resp, err := doRequest(ctx, client, "GET", endpoint, nil)
if err != nil {
return userResponse, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return userResponse, clients.HandleApiError(resp)
}

if err := json.NewDecoder(resp.Body).Decode(&userResponse); err != nil {
return userResponse, fmt.Errorf("decoding response failed: %w", err)
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/resources/resource_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"strings"

"github.com/MaterializeInc/terraform-provider-materialize/pkg/clients"
"github.com/MaterializeInc/terraform-provider-materialize/pkg/frontegg"
"github.com/MaterializeInc/terraform-provider-materialize/pkg/utils"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
Expand Down Expand Up @@ -123,7 +124,7 @@ func userRead(ctx context.Context, d *schema.ResourceData, meta interface{}) dia

userResponse, err := frontegg.ReadUser(ctx, client, userID)
if err != nil {
if strings.Contains(err.Error(), "404") || strings.Contains(strings.ToLower(err.Error()), "not found") {
if clients.IsNotFoundError(err) {
// User doesn't exist, remove from state
d.SetId("")
return nil
Expand Down

0 comments on commit a4371ec

Please sign in to comment.