error.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. // Package googleapi provides helpers for Google-style API responses.
  2. package googleapi
  3. import (
  4. "encoding/json"
  5. "fmt"
  6. "strings"
  7. )
  8. // ErrorResponse represents a Google API error response
  9. type ErrorResponse struct {
  10. Error ErrorDetail `json:"error"`
  11. }
  12. // ErrorDetail contains the error details from Google API
  13. type ErrorDetail struct {
  14. Code int `json:"code"`
  15. Message string `json:"message"`
  16. Status string `json:"status"`
  17. Details []json.RawMessage `json:"details,omitempty"`
  18. }
  19. // ErrorDetailInfo contains additional error information
  20. type ErrorDetailInfo struct {
  21. Type string `json:"@type"`
  22. Reason string `json:"reason,omitempty"`
  23. Domain string `json:"domain,omitempty"`
  24. Metadata map[string]string `json:"metadata,omitempty"`
  25. }
  26. // ErrorHelp contains help links
  27. type ErrorHelp struct {
  28. Type string `json:"@type"`
  29. Links []HelpLink `json:"links,omitempty"`
  30. }
  31. // HelpLink represents a help link
  32. type HelpLink struct {
  33. Description string `json:"description"`
  34. URL string `json:"url"`
  35. }
  36. // ParseError parses a Google API error response and extracts key information
  37. func ParseError(body string) (*ErrorResponse, error) {
  38. var errResp ErrorResponse
  39. if err := json.Unmarshal([]byte(body), &errResp); err != nil {
  40. return nil, fmt.Errorf("failed to parse error response: %w", err)
  41. }
  42. return &errResp, nil
  43. }
  44. // ExtractActivationURL extracts the API activation URL from error details
  45. func ExtractActivationURL(body string) string {
  46. var errResp ErrorResponse
  47. if err := json.Unmarshal([]byte(body), &errResp); err != nil {
  48. return ""
  49. }
  50. // Check error details for activation URL
  51. for _, detailRaw := range errResp.Error.Details {
  52. // Parse as ErrorDetailInfo
  53. var info ErrorDetailInfo
  54. if err := json.Unmarshal(detailRaw, &info); err == nil {
  55. if info.Metadata != nil {
  56. if activationURL, ok := info.Metadata["activationUrl"]; ok && activationURL != "" {
  57. return activationURL
  58. }
  59. }
  60. }
  61. // Parse as ErrorHelp
  62. var help ErrorHelp
  63. if err := json.Unmarshal(detailRaw, &help); err == nil {
  64. for _, link := range help.Links {
  65. if strings.Contains(link.Description, "activation") ||
  66. strings.Contains(link.Description, "API activation") ||
  67. strings.Contains(link.URL, "/apis/api/") {
  68. return link.URL
  69. }
  70. }
  71. }
  72. }
  73. return ""
  74. }
  75. // IsServiceDisabledError checks if the error is a SERVICE_DISABLED error
  76. func IsServiceDisabledError(body string) bool {
  77. var errResp ErrorResponse
  78. if err := json.Unmarshal([]byte(body), &errResp); err != nil {
  79. return false
  80. }
  81. // Check if it's a 403 PERMISSION_DENIED with SERVICE_DISABLED reason
  82. if errResp.Error.Code != 403 || errResp.Error.Status != "PERMISSION_DENIED" {
  83. return false
  84. }
  85. for _, detailRaw := range errResp.Error.Details {
  86. var info ErrorDetailInfo
  87. if err := json.Unmarshal(detailRaw, &info); err == nil {
  88. if info.Reason == "SERVICE_DISABLED" {
  89. return true
  90. }
  91. }
  92. }
  93. return false
  94. }