github.com/newrelic/newrelic-client-go@v1.1.0/internal/http/graphql.go (about)

     1  package http
     2  
     3  import (
     4  	"net/http"
     5  	"strings"
     6  )
     7  
     8  type graphQLRequest struct {
     9  	Query     string                 `json:"query"`
    10  	Variables map[string]interface{} `json:"variables,omitempty"`
    11  }
    12  
    13  type graphQLResponse struct {
    14  	Data interface{} `json:"data"`
    15  }
    16  
    17  // GraphQLError represents a single error.
    18  type GraphQLError struct {
    19  	Message    string   `json:"message,omitempty"`
    20  	Path       []string `json:"path,omitempty"`
    21  	Extensions struct {
    22  		ErrorClass string `json:"errorClass,omitempty"`
    23  		ErrorCode  string `json:"error_code,omitempty"`
    24  		Code       string `json:"code,omitempty"`
    25  	} `json:"extensions,omitempty"`
    26  }
    27  
    28  // GraphQLErrorResponse represents a default error response body.
    29  type GraphQLErrorResponse struct {
    30  	Errors []GraphQLError `json:"errors"`
    31  }
    32  
    33  func (r *GraphQLErrorResponse) Error() string {
    34  	if len(r.Errors) > 0 {
    35  		messages := []string{}
    36  		for _, e := range r.Errors {
    37  			if e.Message != "" {
    38  				messages = append(messages, e.Message)
    39  			}
    40  		}
    41  		return strings.Join(messages, ", ")
    42  	}
    43  
    44  	return ""
    45  }
    46  
    47  // IsNotFound determines if the error is due to a missing resource.
    48  func (r *GraphQLErrorResponse) IsNotFound() bool {
    49  	return false
    50  }
    51  
    52  // IsRetryableError determines if the error is due to a server timeout, or another error that we might want to retry.
    53  func (r *GraphQLErrorResponse) IsRetryableError() bool {
    54  	if len(r.Errors) == 0 {
    55  		return false
    56  	}
    57  
    58  	for _, err := range r.Errors {
    59  		errorClass := err.Extensions.ErrorClass
    60  		if errorClass == "TIMEOUT" || errorClass == "INTERNAL_SERVER_ERROR" || errorClass == "SERVER_ERROR" {
    61  			return true
    62  		}
    63  	}
    64  
    65  	return false
    66  }
    67  
    68  // IsUnauthorized checks a NerdGraph response for a 401 Unauthorize HTTP status code,
    69  // then falls back to check the nested extensions error_code field for `BAD_API_KEY`.
    70  func (r *GraphQLErrorResponse) IsUnauthorized(resp *http.Response) bool {
    71  	if len(r.Errors) == 0 {
    72  		return false
    73  	}
    74  
    75  	if resp.StatusCode == http.StatusUnauthorized {
    76  		return true
    77  	}
    78  
    79  	// Handle invalid or missing API key
    80  	for _, err := range r.Errors {
    81  		if err.Extensions.ErrorCode == "BAD_API_KEY" {
    82  			return true
    83  		}
    84  	}
    85  
    86  	return false
    87  }
    88  
    89  func (r *GraphQLErrorResponse) IsPaymentRequired(resp *http.Response) bool {
    90  	return resp.StatusCode == http.StatusPaymentRequired
    91  }
    92  
    93  // IsDeprecated parses error messages for warnings that a field being used
    94  // is deprecated.  We want to bubble that up, but not stop returning data
    95  //
    96  // Example deprecation message:
    97  //   This field is deprecated! Please use `relatedEntities` instead.
    98  func (r *GraphQLErrorResponse) IsDeprecated() bool {
    99  	for _, err := range r.Errors {
   100  		if strings.HasPrefix(err.Message, "This field is deprecated!") {
   101  			return true
   102  		}
   103  	}
   104  
   105  	return false
   106  }
   107  
   108  // New creates a new instance of GraphQLErrorRepsonse.
   109  func (r *GraphQLErrorResponse) New() ErrorResponse {
   110  	return &GraphQLErrorResponse{}
   111  }