feat(database): support Created By and Last Edited By fields - #480
Conversation
Reviewer's GuideAdds Created By and Last Edited By attribution fields as first-class, read-only database properties, wiring them through row initialization, cell/row/relation updates, grouping/filtering/sorting, user cache handling, UI rendering, and E2E tests while preserving correct web/server attribution semantics. Sequence diagram for database row attribution lifecyclesequenceDiagram
actor Nathan
actor Eva
participant WebApp
participant YDatabaseRow as YDatabaseRow
participant Attribution as attribution_ts
participant MentionCache as useMentionableUsersWithAutoFetch
Nathan->>WebApp: useNewRowDispatch / createRow
WebApp->>YDatabaseRow: initialDatabaseRow(rowId, dbId, rowDoc, uid)
WebApp->>Attribution: initializeRowAttribution(row, uid)
Attribution->>YDatabaseRow: set created_by
Attribution->>YDatabaseRow: set last_edited_by
Eva->>WebApp: useUpdateCellDispatch(rowId, fieldId)
WebApp->>YDatabaseRow: writeCellToRow(..., actorUid)
WebApp->>Attribution: touchRowAttribution(row, uid)
Attribution->>YDatabaseRow: set last_modified
Attribution->>YDatabaseRow: set last_edited_by
WebApp->>AttributionCell: render CreatedBy/LastEditedBy
AttributionCell->>YDatabaseRow: get created_by / last_edited_by
AttributionCell->>Attribution: normalizeAttributionUid(uid)
AttributionCell->>MentionCache: useMentionableUsersWithAutoFetch(shouldFetch, requiredUid)
MentionCache->>WorkspaceService: getMentionableUsers(workspaceId)
MentionCache-->>AttributionCell: mentionableUsers (resolved names)
AttributionCell-->>WebApp: display creator/editor names
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
🥷 Ninja i18n – 🛎️ Translations need to be updatedProject
|
| lint rule | new reports | level | link |
|---|---|---|---|
| Missing translation | 112 | warning | contribute (via Fink 🐦) |
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The new attribution handling logic (e.g., checks for isAttributionFieldType and touchRowAttribution) is duplicated across multiple dispatch implementations like useMoveCardDispatch in both dispatch.ts and dispatch/row.ts; consider centralizing this behavior to avoid future divergence between grid and card flows.
- Person-related filter and picker components now support CreatedBy/LastEditedBy by switching to UID-based identifiers, but they still call useMentionableUsersWithAutoFetch without passing the required UID (unlike AttributionCell); you may want to thread the
requiredUidthrough these usages as well to ensure a one-time refresh when a referenced attribution user is missing from the cache.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new attribution handling logic (e.g., checks for isAttributionFieldType and touchRowAttribution) is duplicated across multiple dispatch implementations like useMoveCardDispatch in both dispatch.ts and dispatch/row.ts; consider centralizing this behavior to avoid future divergence between grid and card flows.
- Person-related filter and picker components now support CreatedBy/LastEditedBy by switching to UID-based identifiers, but they still call useMentionableUsersWithAutoFetch without passing the required UID (unlike AttributionCell); you may want to thread the `requiredUid` through these usages as well to ensure a one-time refresh when a referenced attribution user is missing from the cache.
## Individual Comments
### Comment 1
<location path="src/application/database-yjs/filter.ts" line_range="115-119" />
<code_context>
case FieldType.URL:
case FieldType.Relation:
case FieldType.Person:
+ case FieldType.CreatedBy:
+ case FieldType.LastEditedBy:
group.set(YjsDatabaseKey.content, '');
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard against missing condition snapshots when filtering by CreatedBy/LastEditedBy.
In the new CreatedBy/LastEditedBy branch, `snapshot` is assumed to exist:
```ts
const attribute = fieldType === FieldType.CreatedBy ? YjsDatabaseKey.created_by : YjsDatabaseKey.last_edited_by;
const uid = canonicalizeUserUid(snapshot.row.get(attribute));
```
If `getRowConditionSnapshot` returns `undefined` (e.g. row doc missing/not materialized), this will throw. Elsewhere (e.g. grouping) snapshots are accessed via optional chaining.
To align with that and avoid runtime errors:
```ts
const snapshot = getRowConditionSnapshot(rowDocsForConditions[rowId]);
const attribute = fieldType === FieldType.CreatedBy ? YjsDatabaseKey.created_by : YjsDatabaseKey.last_edited_by;
const uid = canonicalizeUserUid(snapshot?.row.get(attribute));
const userIds = uid === null ? [] : [uid];
```
This keeps filtering safe when snapshots are missing or delayed.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| case FieldType.CreatedBy: | ||
| case FieldType.LastEditedBy: | ||
| try { | ||
| const userIds = JSON.parse(value.content) as string[]; | ||
|
|
There was a problem hiding this comment.
issue (bug_risk): Guard against missing condition snapshots when filtering by CreatedBy/LastEditedBy.
In the new CreatedBy/LastEditedBy branch, snapshot is assumed to exist:
const attribute = fieldType === FieldType.CreatedBy ? YjsDatabaseKey.created_by : YjsDatabaseKey.last_edited_by;
const uid = canonicalizeUserUid(snapshot.row.get(attribute));If getRowConditionSnapshot returns undefined (e.g. row doc missing/not materialized), this will throw. Elsewhere (e.g. grouping) snapshots are accessed via optional chaining.
To align with that and avoid runtime errors:
const snapshot = getRowConditionSnapshot(rowDocsForConditions[rowId]);
const attribute = fieldType === FieldType.CreatedBy ? YjsDatabaseKey.created_by : YjsDatabaseKey.last_edited_by;
const uid = canonicalizeUserUid(snapshot?.row.get(attribute));
const userIds = uid === null ? [] : [uid];This keeps filtering safe when snapshots are missing or delayed.
Description
Adds first-class, read-only Created By and Last Edited By database properties with web/server-compatible row attribution semantics.
The corresponding server/client-api change supplies these fields for imports, default database templates, and server-created rows.
Validation
pnpm type-checkgit diff --checkChecklist
General
Testing
Feature-Specific
Summary by Sourcery
Introduce lossless, read-only row attribution fields that identify who created and most recently edited each database row.
New Features:
Bug Fixes:
Enhancements:
Tests: