errors.go 1.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. package api
  2. import (
  3. "fmt"
  4. "strings"
  5. )
  6. // APIError is a task the gateway refused or could not complete.
  7. //
  8. // It carries the gateway's own code so the caller can react to a specific
  9. // failure (bad credentials, unknown zone, name server mismatch) without
  10. // matching on message text.
  11. type APIError struct {
  12. Code string
  13. Text string
  14. Object string
  15. Stid string
  16. }
  17. func (e *APIError) Error() string {
  18. var b strings.Builder
  19. if e.Code != "" {
  20. fmt.Fprintf(&b, "%s: %s", e.Code, e.Text)
  21. } else {
  22. b.WriteString(e.Text)
  23. }
  24. if e.Object != "" {
  25. fmt.Fprintf(&b, " (object: %s)", e.Object)
  26. }
  27. return b.String()
  28. }
  29. // fromResult builds an error from a failed result block. The actionable code
  30. // and text live in msg, not in status, so msg is preferred.
  31. func fromResult(r Result, stid string) *APIError {
  32. e := &APIError{Stid: stid}
  33. var pick *Msg
  34. for i := range r.Msg {
  35. m := &r.Msg[i]
  36. // A notification ("N") alongside an error ("E") is not the failure.
  37. if strings.EqualFold(m.Type, "error") || strings.HasPrefix(m.Code, "E") {
  38. pick = m
  39. break
  40. }
  41. if pick == nil {
  42. pick = m
  43. }
  44. }
  45. switch {
  46. case pick != nil && pick.Text != "":
  47. e.Code, e.Text = pick.Code, pick.Text
  48. case r.Status.Text != "":
  49. e.Code, e.Text = r.Status.Code, r.Status.Text
  50. default:
  51. e.Text = "the gateway reported a failure without a message"
  52. }
  53. if pick != nil && pick.Object != nil {
  54. e.Object = pick.Object.Type + " " + pick.Object.Value
  55. }
  56. return e
  57. }