Skip to content

Conversation

myieye
Copy link
Collaborator

@myieye myieye commented Aug 20, 2025

Fixes #1944

Copy link

coderabbitai bot commented Aug 20, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

Introduces “without references” add/replace APIs and updates diffing logic to create entries and referenced children before full updates. Adjusts EntrySync to stage reference syncing, adds Entry.WithEntryRefsFrom, and updates tests, including a new scenario syncing a complex-form component that references a newly created sense.

Changes

Cohort / File(s) Summary
Tests: Entry and Diff sequencing
backend/FwLite/FwLiteProjectSync.Tests/EntrySyncTests.cs, backend/FwLite/MiniLcm.Tests/DiffCollectionTests.cs
Added test for syncing a new complex-form component referencing a new sense; updated tests to use AddWithoutReferencesAndGet and ReplaceWithoutReferencesAndGet, renamed a test for new sequencing semantics.
Sync Helpers: Diff flow API and logic
backend/FwLite/MiniLcm/SyncHelpers/DiffCollection.cs
Renamed AddAndGet→AddWithoutReferencesAndGet; added ReplaceWithoutReferencesAndGet; modified DiffAddThenUpdate to stage creation before full updates and to return created/replaced instances for post-update.
Sync Helpers: Entry-specific sequencing
backend/FwLite/MiniLcm/SyncHelpers/EntrySync.cs
Implemented AddWithoutReferencesAndGet and ReplaceWithoutReferencesAndGet for entries to separate non-reference updates from reference attachment; comments updated.
Model
backend/FwLite/MiniLcm/Models/Entry.cs
Added Entry.WithEntryRefsFrom to copy Components/ComplexForms from another entry.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Assessment against linked issues

Objective Addressed Explanation
Prevent FOREIGN KEY failures by ensuring referenced entries (e.g., senses, components) are created before ComplexFormComponent linking (#1944)
Update sync sequencing to separate non-reference creation/replacement from reference attachment (#1944)
Add tests covering complex-form component referencing a newly created sense (#1944)

Assessment against linked issues: Out-of-scope changes

None found.

Possibly related PRs

Suggested labels

💻 FW Lite

Suggested reviewers

  • hahn-kev
  • rmunn

Poem

I hop through diffs with careful cheer,
First make the parts, then link them here.
No foreign keys to make me fret—
Replace without refs, the safest bet.
Carrots aligned, commits in line,
Our sync now hums in perfect time. 🥕

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 1944-project-sync-blocked-foreign-key-constraint-failed

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added the 💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related label Aug 20, 2025
@myieye myieye requested a review from hahn-kev August 20, 2025 18:20
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
backend/FwLite/MiniLcm/Models/Entry.cs (1)

85-88: Avoid list aliasing in WithEntryRefsFrom to prevent unintended mutations

Returning the same list instances from from can lead to aliasing between objects used as "before" and "after" in later diffs. Safer to copy the lists (shallow copy of components is sufficient; you already have .Copy() for deep copy consistency).

Apply this diff:

-    public Entry WithEntryRefsFrom(Entry from)
-    {
-        return this with { Components = from.Components, ComplexForms = from.ComplexForms };
-    }
+    public Entry WithEntryRefsFrom(Entry from)
+    {
+        // copy to avoid aliasing with the source instance
+        return this with
+        {
+            Components = [.. from.Components.Select(c => c.Copy())],
+            ComplexForms = [.. from.ComplexForms.Select(c => c.Copy())]
+        };
+    }
backend/FwLite/FwLiteProjectSync.Tests/EntrySyncTests.cs (1)

228-263: Optionally parameterize entry ordering to harden against ordering variation

Right now the test fixes the entry with the new sense at the end. Since DiffAddThenUpdate’s stage-1 pass processes entries in after order, consider a [Theory] variant that flips the after array order to prove invariance.

backend/FwLite/MiniLcm/SyncHelpers/EntrySync.cs (1)

109-119: Tighten comment and variable naming for clarity in ReplaceWithoutReferencesAndGet

Minor clarity nit: the comment references the old method name, and the variable name can better convey its role as the "before" state for the final replace, carrying only refs from the original before.

Apply this diff:

-        {
-            //same as AddAndGet, but for already existing entries, because they
+        {
+            // same as AddWithoutReferencesAndGet, but for already existing entries, because they
             //might have new entities (e.g. senses) in their hierarchy that other entries reference
             var beforeEntryWithoutEntryRefs = beforeEntry.WithoutEntryRefs();
             var afterEntryWithoutEntryRefs = afterEntry.WithoutEntryRefs();
             var changes = await Sync(beforeEntryWithoutEntryRefs, afterEntryWithoutEntryRefs, api);
             //We've synced everything except the refs
-            var updatedBeforeEntry = afterEntry.WithEntryRefsFrom(beforeEntry);
-            return (changes, updatedBeforeEntry);
+            var beforeForRefs = afterEntry.WithEntryRefsFrom(beforeEntry);
+            return (changes, beforeForRefs);
         }
backend/FwLite/MiniLcm/SyncHelpers/DiffCollection.cs (2)

13-22: Consider XML docs to describe stage-1 semantics and expectations

A brief summary on both methods explaining they perform the “without cross-entity references” phase and must return the object representing the stage-1 “before” state for the subsequent Replace would help future implementers avoid subtle ordering/aliasing bugs.


68-79: Clarify comments to reflect the two-stage process

The comments can be more explicit that this loop performs stage 1 (non-reference creations) and defers stage 2 (full update with references).

Apply this diff:

-                // ensure all children that might be referenced are created
+                // Stage 1: ensure non-reference children (e.g., senses) are created
                 var (changed, replacedEntry) = await diffApi.ReplaceWithoutReferencesAndGet(beforeEntry, afterEntry);
                 changes += changed;
-                postAddUpdates.Add((replacedEntry, afterEntry)); // defer full update
+                postAddUpdates.Add((replacedEntry, afterEntry)); // Stage 2: defer full update (incl. cross-entry refs)
             }
             else
             {
-                // create new entry with children that might be referenced
+                // Stage 1: create new entry with non-reference children (e.g., senses)
                 var (change, created) = await diffApi.AddWithoutReferencesAndGet(afterEntry);
                 changes += change;
-                postAddUpdates.Add((created, afterEntry)); // defer full update
+                postAddUpdates.Add((created, afterEntry)); // Stage 2: defer full update (incl. cross-entry refs)
             }
backend/FwLite/MiniLcm.Tests/DiffCollectionTests.cs (1)

259-273: Reword the explanatory comment to match the actual sequencing

The current comment suggests “new entries must be created first,” but the verified call order shows stage-1 Replace on existing items can safely run before stage-1 Add of new items because references are deferred to stage 2.

Apply this diff:

-        //this order is required because new entries (and/or new senses etc.) must be created first
-        //updated entries might reference the newEntry (or a new sense) and so must be updated after the new entry is created.
-        //the order of the "simple" Replace calls is unimportant.
+        // The key requirement is that all stage-1 operations (without cross-entry refs) complete before any stage-2 Replace.
+        // In our flow, stage-1 Replace can run before stage-1 Add because references are deferred to stage 2.
+        // The order of the final "simple" Replace calls is unimportant.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 16af514 and 8d081b4.

📒 Files selected for processing (5)
  • backend/FwLite/FwLiteProjectSync.Tests/EntrySyncTests.cs (1 hunks)
  • backend/FwLite/MiniLcm.Tests/DiffCollectionTests.cs (3 hunks)
  • backend/FwLite/MiniLcm/Models/Entry.cs (1 hunks)
  • backend/FwLite/MiniLcm/SyncHelpers/DiffCollection.cs (2 hunks)
  • backend/FwLite/MiniLcm/SyncHelpers/EntrySync.cs (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-27T09:24:39.507Z
Learnt from: hahn-kev
PR: sillsdev/languageforge-lexbox#1760
File: backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs:274-277
Timestamp: 2025-06-27T09:24:39.507Z
Learning: In the CrdtMiniLcmApi class, the user prefers to keep the current AddChanges method signature (IEnumerable<IChange>) rather than modifying it to support IAsyncEnumerable for streaming, even when it means materializing collections into memory for bulk operations.

Applied to files:

  • backend/FwLite/MiniLcm/SyncHelpers/DiffCollection.cs
  • backend/FwLite/MiniLcm.Tests/DiffCollectionTests.cs
🧬 Code Graph Analysis (5)
backend/FwLite/MiniLcm/Models/Entry.cs (1)
backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs (1)
  • Entry (648-677)
backend/FwLite/FwLiteProjectSync.Tests/EntrySyncTests.cs (3)
backend/FwLite/MiniLcm/SyncHelpers/DiffCollection.cs (15)
  • Task (12-12)
  • Task (13-17)
  • Task (18-22)
  • Task (23-23)
  • Task (24-24)
  • Task (38-38)
  • Task (39-39)
  • Task (40-40)
  • Task (41-41)
  • Task (54-94)
  • Task (96-123)
  • Task (125-176)
  • Task (259-264)
  • Guid (30-33)
  • Guid (43-46)
backend/FwLite/MiniLcm/SyncHelpers/EntrySync.cs (3)
  • Guid (181-185)
  • Guid (224-228)
  • EntrySync (8-306)
backend/FwLite/MiniLcm/Models/ComplexFormComponent.cs (3)
  • Guid (36-45)
  • ComplexFormComponent (7-22)
  • ComplexFormComponent (53-66)
backend/FwLite/MiniLcm/SyncHelpers/DiffCollection.cs (2)
backend/FwLite/MiniLcm.Tests/DiffCollectionTests.cs (6)
  • Task (158-171)
  • Task (281-285)
  • Task (287-291)
  • Task (293-297)
  • Task (299-303)
  • Task (305-309)
backend/FwLite/MiniLcm/SyncHelpers/EntrySync.cs (8)
  • Task (10-15)
  • Task (17-35)
  • Task (37-46)
  • Task (48-57)
  • Task (59-66)
  • Task (68-75)
  • Task (77-83)
  • Task (100-107)
backend/FwLite/MiniLcm/SyncHelpers/EntrySync.cs (2)
backend/FwLite/MiniLcm/SyncHelpers/DiffCollection.cs (13)
  • Task (12-12)
  • Task (13-17)
  • Task (18-22)
  • Task (23-23)
  • Task (24-24)
  • Task (38-38)
  • Task (39-39)
  • Task (40-40)
  • Task (41-41)
  • Task (54-94)
  • Task (96-123)
  • Task (125-176)
  • Task (259-264)
backend/FwLite/MiniLcm/Models/Entry.cs (3)
  • Entry (43-69)
  • Entry (80-83)
  • Entry (85-88)
backend/FwLite/MiniLcm.Tests/DiffCollectionTests.cs (2)
backend/FwLite/MiniLcm/SyncHelpers/DiffCollection.cs (14)
  • Task (12-12)
  • Task (13-17)
  • Task (18-22)
  • Task (23-23)
  • Task (24-24)
  • Task (38-38)
  • Task (39-39)
  • Task (40-40)
  • Task (41-41)
  • Task (54-94)
  • Task (96-123)
  • Task (125-176)
  • Task (259-264)
  • DiffCollection (49-242)
backend/FwLite/MiniLcm/Models/Entry.cs (3)
  • Entry (43-69)
  • Entry (80-83)
  • Entry (85-88)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build FwHeadless / publish-fw-headless
  • GitHub Check: Analyze (csharp)
  • GitHub Check: frontend-component-unit-tests
  • GitHub Check: Build FW Lite and run tests
  • GitHub Check: frontend
🔇 Additional comments (6)
backend/FwLite/FwLiteProjectSync.Tests/EntrySyncTests.cs (1)

228-263: Excellent targeted test validating FK-safe two-phase sync

This test covers the exact failure mode (component referencing a newly created sense) and demonstrates the two-stage flow preventing FK violations. Assertions look correct with strict ordering and exclusions.

backend/FwLite/MiniLcm/SyncHelpers/EntrySync.cs (1)

100-107: Two-phase creation (without references) is correct and aligns with FK constraints

Creating entries (including senses, example sentences, etc.) without cross-entry refs first is the right approach to avoid FK failures when adding complex-form components.

backend/FwLite/MiniLcm/SyncHelpers/DiffCollection.cs (1)

13-22: New stage-1 hooks (Add/ReplaceWithoutReferencesAndGet) are a good extension point

Default implementations are sensible fallbacks; they enable specialized two-phase behavior where needed (e.g., entries/senses) without forcing it on all call sites.

backend/FwLite/MiniLcm.Tests/DiffCollectionTests.cs (3)

234-236: Updated expectation for Add path looks correct

Verifying AddWithoutReferencesAndGet followed by Replace(created, after) matches the new two-phase contract.


253-256: Updated expectation for Replace path looks correct

Ensuring ReplaceWithoutReferencesAndGet precedes the final Replace aligns with the “creation-before-update” sequence.


299-310: FakeDiffApi overrides reflect the new API surface and sequencing

The test double changes correctly capture and verify call order for stage-1 methods.

Copy link

argos-ci bot commented Aug 20, 2025

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ✅ No changes detected - Aug 22, 2025, 12:12 PM

Copy link

github-actions bot commented Aug 20, 2025

UI unit Tests

  1 files  ±0   40 suites  ±0   23s ⏱️ ±0s
 82 tests ±0   82 ✅ ±0  0 💤 ±0  0 ❌ ±0 
116 runs  ±0  116 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit fa7ac01. ± Comparison against base commit 16af514.

♻️ This comment has been updated with latest results.

Copy link
Collaborator

@hahn-kev hahn-kev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review done, left some comments.

@myieye myieye merged commit 2859918 into develop Aug 26, 2025
18 of 19 checks passed
@myieye myieye deleted the 1944-project-sync-blocked-foreign-key-constraint-failed branch August 26, 2025 08:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Project sync blocked: 'FOREIGN KEY constraint failed'

2 participants