返回 AiToEarn
google-api-error.util.ts
根目录 / project / aitoearn-backend / apps / aitoearn-server / src / core / channels / utils / google-api-error.util.ts
1 import { PlatformErrorCategory } from '../platforms/platforms.exception'
2 import { categoryFromHttpStatus, isHttpStatusRetryable } from './platform-error-classifier.util'
3
4 export interface GoogleApiErrorDetail {
5 reason?: string
6 message?: string
7 domain?: string
8 location?: string
9 locationType?: string
10 }
11
12 export interface GoogleApiError {
13 code?: number
14 message?: string
15 status?: string
16 errors?: GoogleApiErrorDetail[]
17 }
18
19 export interface GoogleApiErrorBody {
20 error?: string | GoogleApiError
21 error_description?: string
22 message?: string
23 }
24
25 export function googleApiPlatformCode(data?: GoogleApiErrorBody): string | number | undefined {
26 const error = googleApiErrorObject(data)
27 return error?.status
28 ?? error?.errors?.[0]?.reason
29 ?? error?.code
30 ?? (typeof data?.error === 'string' ? data.error : undefined)
31 }
32
33 export function googleApiErrorReason(data?: GoogleApiErrorBody): string | undefined {
34 const error = googleApiErrorObject(data)
35 return error?.errors?.[0]?.reason
36 ?? (typeof data?.error === 'string' ? data.error : undefined)
37 }
38
39 export function googleApiPlatformMessage(data?: GoogleApiErrorBody): string | undefined {
40 const error = googleApiErrorObject(data)
41 return error?.errors?.[0]?.message
42 ?? error?.message
43 ?? data?.error_description
44 ?? data?.message
45 ?? (typeof data?.error === 'string' ? data.error : undefined)
46 }
47
48 export function categoryFromGoogleApiError(
49 status?: number,
50 data?: GoogleApiErrorBody,
51 ): PlatformErrorCategory {
52 const error = googleApiErrorObject(data)
53 const apiStatus = error?.status
54 const reason = googleApiErrorReason(data)
55
56 if (isGoogleQuotaReason(reason)) {
57 return PlatformErrorCategory.Quota
58 }
59 if (isGoogleRateLimitReason(reason) || apiStatus === 'RESOURCE_EXHAUSTED' || status === 429) {
60 return PlatformErrorCategory.RateLimit
61 }
62 if (
63 status === 401
64 || data?.error === 'invalid_grant'
65 || data?.error === 'invalid_token'
66 || data?.error === 'unauthorized_client'
67 || data?.error === 'invalid_client'
68 ) {
69 return PlatformErrorCategory.Auth
70 }
71 if (apiStatus === 'PERMISSION_DENIED' || status === 403) {
72 return PlatformErrorCategory.Permission
73 }
74 if (apiStatus === 'NOT_FOUND' || status === 404) {
75 return PlatformErrorCategory.NotFound
76 }
77 if (apiStatus === 'ABORTED' || apiStatus === 'ALREADY_EXISTS' || status === 409) {
78 return PlatformErrorCategory.Conflict
79 }
80 if (apiStatus === 'INVALID_ARGUMENT' || apiStatus === 'FAILED_PRECONDITION' || status === 400 || status === 422) {
81 return PlatformErrorCategory.Validation
82 }
83 if (apiStatus === 'UNAVAILABLE' || apiStatus === 'INTERNAL' || status === undefined || status >= 500) {
84 return PlatformErrorCategory.PlatformUnavailable
85 }
86 return categoryFromHttpStatus(status)
87 }
88
89 export function isGoogleApiErrorRetryable(status?: number, data?: GoogleApiErrorBody): boolean {
90 const error = googleApiErrorObject(data)
91 const apiStatus = error?.status
92 const reason = googleApiErrorReason(data)
93
94 if (isGoogleQuotaReason(reason)) {
95 return false
96 }
97
98 return isGoogleRateLimitReason(reason)
99 || apiStatus === 'RESOURCE_EXHAUSTED'
100 || apiStatus === 'UNAVAILABLE'
101 || apiStatus === 'INTERNAL'
102 || isHttpStatusRetryable(status)
103 }
104
105 function googleApiErrorObject(data?: GoogleApiErrorBody): GoogleApiError | undefined {
106 return typeof data?.error === 'object' ? data.error : undefined
107 }
108
109 function isGoogleRateLimitReason(reason?: string): boolean {
110 return reason === 'rateLimitExceeded'
111 || reason === 'userRateLimitExceeded'
112 }
113
114 function isGoogleQuotaReason(reason?: string): boolean {
115 return reason === 'quotaExceeded'
116 || reason === 'dailyLimitExceeded'
117 || reason === 'uploadLimitExceeded'
118 }
119
119 lines TYPESCRIPT