2023-12-27 00:56:11 +01:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
2023-12-27 01:44:41 +01:00
|
|
|
"encoding/json"
|
2023-12-27 00:56:11 +01:00
|
|
|
"fmt"
|
|
|
|
|
2023-12-27 01:44:41 +01:00
|
|
|
json2 "github.com/aarondl/json"
|
2023-12-27 00:56:11 +01:00
|
|
|
"github.com/getsentry/sentry-go"
|
|
|
|
)
|
|
|
|
|
|
|
|
// An error returned by version 2 of the API.
|
|
|
|
type APIError2 struct {
|
|
|
|
Code int `json:"code"`
|
|
|
|
ID *sentry.EventID `json:"id,omitempty"`
|
|
|
|
Message string `json:"message,omitempty"`
|
|
|
|
Details string `json:"details,omitempty"`
|
|
|
|
Errors map[string][]ModelParseError `json:"errors,omitempty"`
|
|
|
|
|
|
|
|
RatelimitReset *int `json:"ratelimit_reset,omitempty"`
|
|
|
|
|
|
|
|
Status int `json:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e APIError2) Error() string {
|
|
|
|
if e.Message == "" {
|
|
|
|
e.Message = errCodeMessages[e.Code]
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.Details != "" {
|
|
|
|
return fmt.Sprintf("%s (code: %d) (%s)", e.Message, e.Code, e.Details)
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf("%s (code: %d)", e.Message, e.Code)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a new error in APIv2 format.
|
|
|
|
func NewV2Error(code int, details string, parseErrors ...ModelParseError) APIError2 {
|
|
|
|
var errors map[string][]ModelParseError
|
|
|
|
if len(parseErrors) != 0 {
|
|
|
|
errors = make(map[string][]ModelParseError)
|
|
|
|
for _, p := range parseErrors {
|
|
|
|
errors[p.Key] = append(errors[p.Key], p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return APIError2{
|
|
|
|
Code: code,
|
|
|
|
Message: errCodeMessages[code],
|
|
|
|
Details: details,
|
|
|
|
Errors: errors,
|
|
|
|
Status: errCodeStatuses[code],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type ModelParseError struct {
|
|
|
|
Message string `json:"message,omitempty"`
|
|
|
|
MaxLength int `json:"max_length,omitempty"`
|
|
|
|
ActualLength int `json:"actual_length,omitempty"`
|
|
|
|
|
|
|
|
ExpectedValues []any `json:"expected_values,omitempty"`
|
|
|
|
ActualValue any `json:"actual_value,omitempty"`
|
|
|
|
|
2023-12-27 01:44:41 +01:00
|
|
|
ExpectedType string `json:"expected_type,omitempty"`
|
|
|
|
ActualType string `json:"actual_type,omitempty"`
|
|
|
|
|
2023-12-27 00:56:11 +01:00
|
|
|
Key string `json:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewModelParseError(key, message string) ModelParseError {
|
|
|
|
return NewModelParseErrorWithLength(key, message, 0, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewModelParseErrorWithLength(key, message string, maxLength, actualLength int) ModelParseError {
|
|
|
|
return ModelParseError{
|
|
|
|
Key: key,
|
|
|
|
Message: message,
|
|
|
|
MaxLength: maxLength,
|
|
|
|
ActualLength: actualLength,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewModelParseErrorWithValues(key, message string, expectedValues []any, actualValue any) ModelParseError {
|
|
|
|
return ModelParseError{
|
|
|
|
Key: key,
|
|
|
|
Message: message,
|
|
|
|
ExpectedValues: expectedValues,
|
|
|
|
ActualValue: actualValue,
|
|
|
|
}
|
|
|
|
}
|
2023-12-27 01:44:41 +01:00
|
|
|
|
|
|
|
func ParseJSONError(err error) *ModelParseError {
|
|
|
|
switch pe := err.(type) {
|
|
|
|
case *json.UnmarshalTypeError:
|
|
|
|
key := pe.Field
|
|
|
|
if key == "" {
|
|
|
|
key = "parse"
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ModelParseError{
|
|
|
|
Key: key,
|
|
|
|
ExpectedType: pe.Type.String(),
|
|
|
|
ActualType: pe.Value,
|
|
|
|
}
|
|
|
|
case *json2.UnmarshalTypeError:
|
2023-12-28 02:46:08 +01:00
|
|
|
// TODO: remove the need for this, somehow
|
|
|
|
// This seems to be a bug in the
|
2023-12-27 01:44:41 +01:00
|
|
|
key := pe.Field
|
|
|
|
if key == "" {
|
|
|
|
key = "parse"
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ModelParseError{
|
|
|
|
Key: key,
|
2023-12-28 02:46:08 +01:00
|
|
|
Message: "Invalid type passed for a field. Make sure your types match those in the documentation.",
|
2023-12-27 01:44:41 +01:00
|
|
|
ExpectedType: pe.Type.String(),
|
|
|
|
ActualType: pe.Value,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|