Skip to content

Commit 531ae54

Browse files
committed
fix(sailpoint): adaptive token TTL buffer and honest search-count on missing header
- Cap the token cache expiry buffer at the smaller of 60s and 10% of the lifetime so a short-lived token still caches instead of expiring immediately. - Return an error for search_count when SailPoint provides no X-Total-Count (and no numeric body) instead of reporting a misleading total of 0. Tests added for both count paths.
1 parent e1b8784 commit 531ae54

3 files changed

Lines changed: 58 additions & 5 deletions

File tree

‎apps/sim/app/api/tools/sailpoint/client.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,12 @@ export async function getSailPointAccessToken(creds: SailPointServerCredentials)
185185

186186
const parsedExpiry = Number(data.expires_in)
187187
const expiresInSec = Number.isFinite(parsedExpiry) && parsedExpiry > 0 ? parsedExpiry : 3600
188+
// Use the smaller of the 60s buffer and 10% of the lifetime so a short-lived token (expires_in
189+
// under ~60s) still gets cached for most of its life instead of expiring immediately.
190+
const bufferMs = Math.min(TOKEN_EXPIRY_BUFFER_MS, expiresInSec * 100)
188191
tokenCache.set(key, {
189192
token: data.access_token,
190-
expiresAt: Date.now() + Math.max(expiresInSec * 1000 - TOKEN_EXPIRY_BUFFER_MS, 0),
193+
expiresAt: Date.now() + Math.max(expiresInSec * 1000 - bufferMs, 0),
191194
})
192195
return data.access_token
193196
}

‎apps/sim/app/api/tools/sailpoint/query/route.test.ts‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,46 @@ describe('SailPoint query route', () => {
124124
expect(data.output.results).toEqual([{ _type: 'identity', id: 'i1' }])
125125
})
126126

127+
it('returns the total from search count', async () => {
128+
fetchMock
129+
.mockResolvedValueOnce(tokenResponse())
130+
.mockResolvedValueOnce(
131+
new Response(null, { status: 204, headers: { 'X-Total-Count': '42' } })
132+
)
133+
134+
const request = createMockRequest('POST', {
135+
...baseCreds,
136+
tenant: 'acme-count',
137+
operation: 'sailpoint_search_count',
138+
indices: 'identities',
139+
})
140+
141+
const response = await POST(request)
142+
const data = await response.json()
143+
144+
expect(response.status).toBe(200)
145+
expect(data.output).toEqual({ total: 42 })
146+
})
147+
148+
it('errors instead of reporting zero when search count has no total header', async () => {
149+
fetchMock
150+
.mockResolvedValueOnce(tokenResponse())
151+
.mockResolvedValueOnce(new Response(null, { status: 204 }))
152+
153+
const request = createMockRequest('POST', {
154+
...baseCreds,
155+
tenant: 'acme-count-none',
156+
operation: 'sailpoint_search_count',
157+
indices: 'identities',
158+
})
159+
160+
const response = await POST(request)
161+
const data = await response.json()
162+
163+
expect(response.status).toBe(502)
164+
expect(data.success).toBe(false)
165+
})
166+
127167
it('preserves the aggregation object returned by /search/aggregate', async () => {
128168
const aggregationResult = {
129169
aggregations: { department: { buckets: [{ key: 'Finance', count: 12 }] } },

‎apps/sim/app/api/tools/sailpoint/query/route.ts‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,22 @@ async function execute(
145145
case 'item':
146146
output = { item: result.data ?? null }
147147
break
148-
case 'count':
149-
output = {
150-
total:
151-
readTotalCount(result.headers) ?? (typeof result.data === 'number' ? result.data : 0),
148+
case 'count': {
149+
const total =
150+
readTotalCount(result.headers) ?? (typeof result.data === 'number' ? result.data : null)
151+
// Do not report an unknown count as 0 - that reads as "no matches". Surface it as an error.
152+
if (total === null) {
153+
return NextResponse.json(
154+
{
155+
success: false,
156+
error: 'SailPoint did not return a total count (X-Total-Count missing)',
157+
},
158+
{ status: 502 }
159+
)
152160
}
161+
output = { total }
153162
break
163+
}
154164
case 'write':
155165
output = { accepted: result.ok, status: result.status }
156166
break

0 commit comments

Comments
 (0)