Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code c903d614ca Unhandled pendingActivationUser auth context type in buildActorMetadata causes mutation failure during onboarding
https://sonarly.com/issue/14356?type=bug

Users in the onboarding/profile creation flow get a `pendingActivationUser` auth context that is not handled by `buildActorMetadata()`, causing all write mutations (like `UpdateOneWorkspaceMember`) to fail with an unhandled error.

Fix: Added handling for the `pendingActivationUser` auth context type in `buildActorMetadata()`. When a user is in the onboarding flow (at `/create/profile`), they don't yet have a `workspaceMember` record, so the middleware creates a `pendingActivationUser` auth context instead of a `user` context. The fix builds actor metadata from the user entity's `firstName`/`lastName` fields (available on `PendingActivationUserWorkspaceAuthContext.user`) with an empty `workspaceMemberId` since no workspace member exists yet.

Also improved the fallback error message to include `authContext.type`, so any future unhandled context types (e.g., `system`) will produce a more actionable error message for debugging.

Updated the existing test to cover the new `pendingActivationUser` case and updated the `system` type error assertion to match the new error message format.
2026-03-13 11:04:25 +00:00
Sonarly Claude Code 311a5f7e18 SSE optimistic update crashes on custom fields not yet in stale metadata cache
https://sonarly.com/issue/14352?type=bug

When a user creates custom fields on an object and then views that object's records, incoming SSE database events include the new fields, but the frontend metadata cache hasn't refreshed yet, causing a crash in the optimistic record computation.

Fix: **What changed:** In `computeOptimisticRecordFromInput.ts`, replaced the `throw new Error(...)` with `console.warn(...)` when unknown fields are detected in the record input (line 73-77).

**Why:** The function validates that all fields in `recordInput` exist in `objectMetadataItem.fields`. This validation was originally written for mutation-triggered optimistic updates where the frontend controls the input. When SSE real-time events were added (commit `d51c988a9e`, Jan 2026), this function started receiving server-controlled input that may include fields not yet known to the frontend's metadata cache — specifically when a user creates custom fields and immediately views records before the metadata refreshes.

The function's main for-loop (line 80) iterates only over `objectMetadataItem.fields`, so unknown fields in `recordInput` are already naturally skipped. The throw was a defensive assertion that became incorrect when the caller contract changed. Converting it to a warning preserves observability while allowing graceful degradation.

**Test update:** Updated the existing test from asserting a throw to asserting a `console.warn` call and verifying that known fields (like `city: 'Paris'`) are still correctly processed while unknown fields are skipped.
2026-03-13 10:59:58 +00:00
2 changed files with 43 additions and 2 deletions
@@ -147,6 +147,36 @@ describe('ActorFromAuthContextService', () => {
]);
});
it('should build metadata from user when pendingActivationUser auth context', async () => {
const authContext = {
type: 'pendingActivationUser',
userWorkspaceId: '20202020-1234-5678-9012-345678901234',
user: {
id: '20202020-9aae-49a8-bafc-ac44bae62d6d',
firstName: 'Simone',
lastName: 'Ergotino',
},
workspace: { id: '20202020-bdec-497f-847a-1bb334fefe58' },
} as unknown as WorkspaceAuthContext;
const result = await service.injectCreatedBy({
records: [{}],
objectMetadataNameSingular: 'person',
authContext,
});
expect(result).toEqual<ExpectedResult>([
{
createdBy: {
context: {},
name: 'Simone Ergotino',
workspaceMemberId: '',
source: FieldActorSource.MANUAL,
},
},
]);
});
it('should throw error when no valid actor information is found', async () => {
const authContext = {
type: 'system',
@@ -160,7 +190,7 @@ describe('ActorFromAuthContextService', () => {
authContext,
}),
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to build actor metadata - no valid actor information found in auth context"`,
`"Unable to build actor metadata - unhandled auth context type: system"`,
);
});
});
@@ -8,6 +8,7 @@ import { buildCreatedByFromApplication } from 'src/engine/core-modules/actor/uti
import { buildCreatedByFromFullNameMetadata } from 'src/engine/core-modules/actor/utils/build-created-by-from-full-name-metadata.util';
import { isApiKeyAuthContext } from 'src/engine/core-modules/auth/guards/is-api-key-auth-context.guard';
import { isApplicationAuthContext } from 'src/engine/core-modules/auth/guards/is-application-auth-context.guard';
import { isPendingActivationUserAuthContext } from 'src/engine/core-modules/auth/guards/is-pending-activation-user-auth-context.guard';
import { isUserAuthContext } from 'src/engine/core-modules/auth/guards/is-user-auth-context.guard';
import { type WorkspaceAuthContext } from 'src/engine/core-modules/auth/types/workspace-auth-context.type';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
@@ -175,8 +176,18 @@ export class ActorFromAuthContextService {
});
}
if (isPendingActivationUserAuthContext(authContext)) {
return buildCreatedByFromFullNameMetadata({
fullNameMetadata: {
firstName: authContext.user.firstName,
lastName: authContext.user.lastName,
},
workspaceMemberId: '',
});
}
throw new Error(
'Unable to build actor metadata - no valid actor information found in auth context',
`Unable to build actor metadata - unhandled auth context type: ${authContext.type}`,
);
}
}