fix: let TIC lookup run during onboarding + tolerate lowercase TIC status (#346)
* fix: let TIC lookup run during onboarding; tolerate lowercase TIC status Two bugs found in prod testing of the BankID picker: 1. Extension dispatcher required a resolved company context for every non-skipAuth route. /api/extensions/ext/tic/lookup is hit by Step2CompanyDetails' debounced fetcher (and the BankID picker's one-click path) during onboarding — before the user has a company — so requireCompanyId threw "No company context" and the call 500'd. Added a `skipCompanyContext` flag to ApiRouteDefinition. Marks /lookup and /profile on the TIC extension so they bypass company resolution but still require auth. Handlers don't use ctx for these routes, so no downstream changes were needed. 2. TIC enrichment has been observed returning lowercase 'failed' (and presumably other lowercase status values). The previous `=== 'Completed'` strict-case check would silently reject even a legitimately completed enrichment if TIC normalizes to lowercase. Now compares case-insensitively against 'completed' and 'partiallycompleted'. On non-usable enrichment, we now log the full response shape (minus the time-limited secureUrl token) so we can diagnose why real-user enrichments come back failed — useful for debugging TIC tenant config issues where status='failed' but no documented error field is set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: reject skipAuth + skipCompanyContext combination (PR review) Greptile P2 finding: if a future route accidentally sets both flags, skipAuth fires first and silently drops the auth requirement that skipCompanyContext implicitly assumes. No current route combines them, but this prevents the mistake from reaching prod. - Dispatcher throws 500 at matching time if both flags are set, with a descriptive log line naming the misconfigured route. - Type JSDoc now lists the three mutually-exclusive modes upfront and marks the combination as explicitly forbidden. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
8fd3f112f8
commit
1014d7cc2c
@@ -49,11 +49,21 @@ async function fetchAndStoreEnrichment(
|
||||
hasSecureUrl: !!enrichment.secureUrl,
|
||||
})
|
||||
|
||||
// Accept both fully and partially completed runs — if the tenant only has
|
||||
// SPAR enabled (not CompanyRoles), we still want the address data.
|
||||
const usable = (enrichment.status === 'Completed' || enrichment.status === 'PartiallyCompleted')
|
||||
&& enrichment.secureUrl
|
||||
if (!usable) return
|
||||
// Case-insensitive status comparison: TIC has been observed returning
|
||||
// lowercase values ('completed', 'failed') in addition to the docs' canonical
|
||||
// capitalized form. Accept both fully and partially completed runs — if the
|
||||
// tenant only has SPAR enabled (not CompanyRoles) we still want the address.
|
||||
const statusLower = String(enrichment.status ?? '').toLowerCase()
|
||||
const isCompleted = statusLower === 'completed' || statusLower === 'partiallycompleted'
|
||||
const usable = isCompleted && enrichment.secureUrl
|
||||
if (!usable) {
|
||||
// Log the full response shape (sans secureUrl — time-limited token)
|
||||
// so we can diagnose why a real-user enrichment comes back non-usable.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { secureUrl: _omit, ...responseDiagnostic } = enrichment
|
||||
log.warn('enrichment not usable — inspect response for diagnostic fields', responseDiagnostic)
|
||||
return
|
||||
}
|
||||
|
||||
const enrichmentData = await fetchEnrichmentData(enrichment.secureUrl)
|
||||
|
||||
@@ -193,6 +203,10 @@ export const ticExtension: Extension = {
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/lookup',
|
||||
// Used during onboarding (Step2CompanyDetails debounced lookup + the
|
||||
// BankID picker) — user is authenticated but does not yet have a
|
||||
// company. Must not require a company context.
|
||||
skipCompanyContext: true,
|
||||
handler: async (request: Request, ctx?) => {
|
||||
const log = ctx?.log ?? console
|
||||
const url = new URL(request.url)
|
||||
@@ -305,6 +319,9 @@ export const ticExtension: Extension = {
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/profile',
|
||||
// Used during onboarding to render richer company profile details —
|
||||
// user is authenticated but may not yet have a company. See /lookup.
|
||||
skipCompanyContext: true,
|
||||
handler: async (request: Request, ctx?) => {
|
||||
const log = ctx?.log ?? console
|
||||
const url = new URL(request.url)
|
||||
|
||||
Reference in New Issue
Block a user