Skip to content

Commit 5ee3a4a

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(selectors): recheck post-resolution cancellation
1 parent b7f5eff commit 5ee3a4a

4 files changed

Lines changed: 60 additions & 0 deletions

File tree

‎apps/sim/lib/selectors/server/credentials.test.ts‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,4 +226,34 @@ describe('resolveSelectorOAuthAccessToken', () => {
226226

227227
expect(mocks.resolveCredentialTokenBundle).not.toHaveBeenCalled()
228228
})
229+
230+
it('rechecks cancellation before consuming a fulfilled credential result', async () => {
231+
mocks.resolveCredentialTokenBundle.mockResolvedValue({
232+
accessToken: 'fulfilled-access-token',
233+
})
234+
const controller = new AbortController()
235+
const protectedValues = createSelectorProtectedValues()
236+
const recordCredentialUse = vi.fn()
237+
const abortReason = new DOMException('Selector request canceled', 'AbortError')
238+
239+
const pending = resolveSelectorOAuthAccessToken({
240+
credential: {
241+
suppliedId: 'credential-1',
242+
access: {
243+
ok: true,
244+
credentialOwnerUserId: 'owner-1',
245+
resolvedCredentialId: 'credential-1',
246+
},
247+
signal: controller.signal,
248+
},
249+
serviceId: 'gmail',
250+
protectedValues,
251+
recordCredentialUse,
252+
})
253+
queueMicrotask(() => controller.abort(abortReason))
254+
255+
await expect(pending).rejects.toBe(abortReason)
256+
expect(protectedValues.contains('fulfilled-access-token')).toBe(false)
257+
expect(recordCredentialUse).not.toHaveBeenCalled()
258+
})
229259
})

‎apps/sim/lib/selectors/server/credentials.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ export async function resolveSelectorOAuthAccessToken(input: {
170170
),
171171
input.credential.signal
172172
)
173+
input.credential.signal?.throwIfAborted()
173174
const token = result?.accessToken
174175

175176
if (!token) throw new SelectorConnectionUnavailableError()

‎apps/sim/lib/selectors/server/providers/credential-bundle.test.ts‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,32 @@ describe('selector credential bundles', () => {
7070
{ privacyMode: 'selector' }
7171
)
7272
})
73+
74+
it('rechecks cancellation before consuming a fulfilled credential bundle', async () => {
75+
mockResolveCredentialAccessToken.mockResolvedValue({
76+
accessToken: 'fulfilled-access-token',
77+
cloudId: 'cloud-1',
78+
})
79+
const controller = new AbortController()
80+
const protectedValues = createSelectorProtectedValues()
81+
const recordCredentialUse = vi.fn()
82+
const abortReason = new DOMException('Selector request canceled', 'AbortError')
83+
84+
const pending = resolveSelectorCredentialBundle({
85+
credential: {
86+
suppliedId: 'credential-1',
87+
access: { ok: true, credentialOwnerUserId: 'owner-1' },
88+
signal: controller.signal,
89+
},
90+
protectedValues,
91+
providerId: 'atlassian',
92+
recordCredentialUse,
93+
})
94+
queueMicrotask(() => controller.abort(abortReason))
95+
96+
await expect(pending).rejects.toBe(abortReason)
97+
expect(protectedValues.contains('fulfilled-access-token')).toBe(false)
98+
expect(protectedValues.contains('cloud-1')).toBe(false)
99+
expect(recordCredentialUse).not.toHaveBeenCalled()
100+
})
73101
})

‎apps/sim/lib/selectors/server/providers/credential-bundle.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export async function resolveSelectorCredentialBundle(input: {
4848
),
4949
credential.signal
5050
)
51+
credential.signal?.throwIfAborted()
5152
} catch (error) {
5253
if (credential.signal?.aborted) throw error
5354
throw new SelectorConnectionUnavailableError()

0 commit comments

Comments
 (0)