From 1729378a652f97e33ceded17c1b6735888670702 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Mon, 6 Apr 2026 17:58:13 -0400 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20perf:=20Separate=20Error=20Handling?= =?UTF-8?q?=20for=20Principal=20Resolution=20vs=20Config=20Overrides=20(#1?= =?UTF-8?q?2550)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Distinguish between buildPrincipals and getApplicableConfigs failures so the uncached fallback to baseConfig is intentional and logged separately from config override errors. --- packages/api/src/app/service.spec.ts | 12 ++++++++++++ packages/api/src/app/service.ts | 16 +++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/packages/api/src/app/service.spec.ts b/packages/api/src/app/service.spec.ts index f881b87354..03e9a60d73 100644 --- a/packages/api/src/app/service.spec.ts +++ b/packages/api/src/app/service.spec.ts @@ -254,6 +254,18 @@ describe('createAppConfigService', () => { expect(config).toEqual(deps._baseConfig); }); + it('falls back to base config on buildPrincipals error', async () => { + const deps = createDeps({ + getUserPrincipals: jest.fn().mockRejectedValue(new Error('principal lookup failed')), + }); + const { getAppConfig } = createAppConfigService(deps); + + const config = await getAppConfig({ userId: 'uid1', role: 'USER' }); + + expect(deps.getApplicableConfigs).not.toHaveBeenCalled(); + expect(config).toEqual(deps._baseConfig); + }); + it('falls back to base config on getApplicableConfigs error', async () => { const deps = createDeps({ getApplicableConfigs: jest.fn().mockRejectedValue(new Error('DB down')), diff --git a/packages/api/src/app/service.ts b/packages/api/src/app/service.ts index 29fda7df92..237a9f13f4 100644 --- a/packages/api/src/app/service.ts +++ b/packages/api/src/app/service.ts @@ -168,14 +168,20 @@ export function createAppConfigService(deps: AppConfigServiceDeps) { } } + let principals: Array<{ principalType: string; principalId?: string | Types.ObjectId }>; try { - const principals = await buildPrincipals(role, userId); + principals = await buildPrincipals(role, userId); + } catch (error) { + logger.error('[getAppConfig] Error building principals, falling back to base:', error); + return baseConfig; + } - if (principals.length === 0) { - await cache.set(cacheKey, baseConfig, overrideCacheTtl); - return baseConfig; - } + if (principals.length === 0) { + await cache.set(cacheKey, baseConfig, overrideCacheTtl); + return baseConfig; + } + try { const configs = await getApplicableConfigs(principals); if (configs.length === 0) {