Skip to content

Commit c723a3a

Browse files
fix(knowledge): resolve Drive shortcuts during connector sync (#7701)
1 parent cca39b6 commit c723a3a

8 files changed

Lines changed: 1168 additions & 101 deletions

File tree

apps/sim/connectors/google-drive/google-drive-errors.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import {
55
resolveRetryDelayMs,
66
retryWithExponentialBackoff,
77
} from '@/lib/knowledge/documents/utils'
8+
import {
9+
ConnectorSourceError,
10+
type ConnectorSourceFailureCategory,
11+
} from '@/connectors/source-error'
812
import { readBodyWithLimit } from '@/connectors/utils'
913

1014
const GOOGLE_ERROR_BODY_MAX_BYTES = 64 * 1024
@@ -103,22 +107,47 @@ function classifyGoogleDriveError(
103107
return 'unknown'
104108
}
105109

106-
export class GoogleDriveApiError extends Error {
110+
function diagnosticCategory(
111+
kind: GoogleDriveErrorKind,
112+
status: number
113+
): ConnectorSourceFailureCategory | undefined {
114+
switch (kind) {
115+
case 'authorization':
116+
case 'permission':
117+
return 'authorization'
118+
case 'not_found':
119+
return 'source_unavailable'
120+
case 'export_too_large':
121+
case 'unsupported_export':
122+
case 'policy':
123+
return 'request_rejected'
124+
case 'quota':
125+
return 'rate_limit'
126+
case 'transient':
127+
return status === 429 || status === 403 ? 'rate_limit' : 'provider_unavailable'
128+
default:
129+
return undefined
130+
}
131+
}
132+
133+
export class GoogleDriveApiError extends ConnectorSourceError {
107134
retryAfterMs?: number
108135
readonly reasons: readonly string[]
109136
readonly kind: GoogleDriveErrorKind
110137
readonly rateLimited: boolean
111138

112-
constructor(
113-
readonly status: number,
114-
normalizedReasons: readonly string[]
115-
) {
139+
constructor(status: number, normalizedReasons: readonly string[]) {
116140
const diagnosticReasons = normalizedReasons.slice(0, GOOGLE_ERROR_REASON_MAX_COUNT)
117141
const reasonSuffix = diagnosticReasons.length > 0 ? ` (${diagnosticReasons.join(', ')})` : ''
118-
super(`Google Drive API request failed with HTTP ${status}${reasonSuffix}`)
142+
const kind = classifyGoogleDriveError(status, normalizedReasons)
143+
super(
144+
`Google Drive API request failed with HTTP ${status}${reasonSuffix}`,
145+
status,
146+
diagnosticCategory(kind, status)
147+
)
119148
this.name = 'GoogleDriveApiError'
120149
this.reasons = diagnosticReasons
121-
this.kind = classifyGoogleDriveError(status, normalizedReasons)
150+
this.kind = kind
122151
this.rateLimited =
123152
status === 429 || normalizedReasons.some((reason) => RATE_LIMIT_REASONS.has(reason))
124153
}

apps/sim/connectors/google-drive/google-drive.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -928,8 +928,12 @@ describe('Google Drive change feed', () => {
928928
{ kind: 'removed', externalId: 'moved-out' },
929929
{ kind: 'removed', externalId: 'video' },
930930
])
931-
expect(result.nextCursor).toBe('5000')
932-
expect(result.hasMore).toBe(false)
931+
expect(result.nextCursor).toMatch(/^gdrive-shortcuts:v1:/)
932+
expect(result.hasMore).toBe(true)
933+
mockFetch.mockResolvedValueOnce(jsonResponse({ files: [] }))
934+
await expect(
935+
googleDriveConnector.listChanges!('token', {}, result.nextCursor!)
936+
).resolves.toEqual({ changes: [], nextCursor: '5000', hasMore: false })
933937
const url = new URL(String(mockFetch.mock.calls[0][0]))
934938
expect(url.searchParams.get('pageToken')).toBe('4821')
935939
expect(url.searchParams.get('includeRemoved')).toBe('true')

0 commit comments

Comments
 (0)