Skip to content

Conversation

myieye
Copy link
Collaborator

@myieye myieye commented Aug 21, 2025

Inspired by:
image

Copy link

coderabbitai bot commented Aug 21, 2025

📝 Walkthrough

Walkthrough

Public create methods in FwDataMiniLcmApi were made non-async and refactored to use Task.FromResult; collections use spread syntax; translation/multistring nulls map to empty arrays. Minor refactors in progress utilities, MAUI warning suppression, import API signature default removal, project services constructor/logging cleanup, controller catch tweak, and test async write.

Changes

Cohort / File(s) Summary of changes
MiniLcm write API and mappings
backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
Converted several public create methods from async to synchronous Task-returning; replaced awaits with Task.FromResult; switched ToList() constructions to collection spread syntax; normalized translation/multistring nulls to empty arrays; minor variable/style refactors; ensured complex form entry reference fallback.
Threaded progress utility
backend/FwLite/FwDataMiniLcmBridge/LcmUtils/LcmThreadedProgress.cs
Made backing field readonly; added pragma to suppress CS0067; converted select getters to expression-bodied properties; no functional changes to progress logic.
MAUI permissions warning suppression
backend/FwLite/FwLiteMaui/MainPage.xaml.Windows.cs
Expanded pragma to suppress VSTHRD110 and CS4014 around unawaited calls; removed an inline comment; behavior unchanged.
Resumable import API signature
backend/FwLite/FwLiteProjectSync/Import/ResumableImportApi.cs
Removed default value for BetweenPosition parameter in explicit interface implementation of CreateWritingSystem; forwarding logic unchanged.
Project services provider cleanup
backend/FwLite/FwLiteShared/Services/ProjectServicesProvider.cs
Removed ILogger parameter from constructor; simplified disposal (null-conditional disposes) and removed a disposal log line; scope removal unchanged.
Sync controller catch refinement
backend/LexBoxApi/Controllers/SyncController.cs
Simplified catch of OperationCanceledException to unnamed variable; response unchanged.
Test adjustments
backend/Testing/FwHeadless/MediaFileServiceTests.cs
Removed unused usings; switched a MemoryStream write to WriteAsync in an async test; no behavioral change.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • hahn-kev
  • rmunn

Poem

I twitched my whiskers, merged with glee,
Turned awaits to Tasks quite breezily.
Lists now blossom with tidy spreads,
Exceptions caught with lighter threads.
Logs hop out, tests hop in—
A carrot-clean refactor win! 🥕🐇

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dotnet-warning-format-cleanup

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 💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related 📦 Lexbox issues related to any server side code, fw-headless included labels Aug 21, 2025
Copy link

argos-ci bot commented Aug 21, 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, 7:59 AM

Copy link

github-actions bot commented Aug 21, 2025

C# Unit Tests

130 tests   130 ✅  19s ⏱️
 20 suites    0 💤
  1 files      0 ❌

Results for commit 726d8ff.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Aug 21, 2025

UI unit Tests

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

Results for commit 726d8ff.

♻️ This comment has been updated with latest results.

@myieye myieye marked this pull request as ready for review August 22, 2025 09:04
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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/FwLite/FwLiteShared/Services/ProjectServicesProvider.cs (1)

153-164: Dispose underlying JS-invokables before disposing their DotNetObjectReferences

You now null-guard the disposals, which is good. However, disposing DotNetObjectReference alone doesn't dispose the wrapped object. MiniLcm is correctly disposed via .Value.Dispose() before .Dispose(). Do the same for HistoryService and SyncService to avoid leaks.

             logger.LogInformation("Disposing project scope {ProjectName}", projectName);
             projectServicesProvider._projectScopes.TryRemove(this, out _);
-            HistoryService?.Dispose();
-            SyncService?.Dispose();
+            if (HistoryService?.Value is IAsyncDisposable hsAsync) await hsAsync.DisposeAsync();
+            else (HistoryService?.Value as IDisposable)?.Dispose();
+            HistoryService?.Dispose();
+
+            if (SyncService?.Value is IAsyncDisposable ssAsync) await ssAsync.DisposeAsync();
+            else (SyncService?.Value as IDisposable)?.Dispose();
+            SyncService?.Dispose();
 
             MiniLcm.Value.Dispose();
             MiniLcm.Dispose();
🧹 Nitpick comments (7)
backend/Testing/FwHeadless/MediaFileServiceTests.cs (1)

235-243: Avoid size-check flakiness from StreamWriter buffering; write bytes directly (or flush) while filling the MemoryStream

Using StreamWriter to grow MemoryStream means memoryStream.Length may not reflect recent writes until the writer flushes, which can make the loop run longer than intended. Your switch to WriteAsync is fine, but the buffering caveat remains. Prefer writing bytes directly to the MemoryStream (or force flushing) to make the size check precise and the test faster/deterministic.

Option A (minimal): enable AutoFlush so Length is updated on each write.

-        using (var stream = new StreamWriter(memoryStream, leaveOpen: true))
+        using (var stream = new StreamWriter(memoryStream, leaveOpen: true))
         {
+            stream.AutoFlush = true;
             while (memoryStream.Length < _fwHeadlessConfig.MaxUploadFileSizeBytes)
             {
                 await stream.WriteAsync(Guid.NewGuid().ToString("N"));
             }
         }

Option B (preferred): remove StreamWriter and write random bytes directly.

-        var memoryStream = new MemoryStream();
-        using (var stream = new StreamWriter(memoryStream, leaveOpen: true))
-        {
-            while (memoryStream.Length < _fwHeadlessConfig.MaxUploadFileSizeBytes)
-            {
-                await stream.WriteAsync(Guid.NewGuid().ToString("N"));
-            }
-        }
+        var memoryStream = new MemoryStream();
+        var buffer = new byte[8192];
+        var rng = System.Security.Cryptography.RandomNumberGenerator.Create();
+        while (memoryStream.Length <= _fwHeadlessConfig.MaxUploadFileSizeBytes)
+        {
+            rng.GetBytes(buffer);
+            var remaining = (int)Math.Min(buffer.Length,
+                _fwHeadlessConfig.MaxUploadFileSizeBytes - memoryStream.Length + 1);
+            await memoryStream.WriteAsync(buffer.AsMemory(0, remaining));
+        }
backend/FwLite/FwLiteMaui/MainPage.xaml.Windows.cs (1)

30-41: LGTM: suppression expanded to CS4014 to document intentional fire-and-forget

This matches the established pattern for SetPermissionStateAsync being intentionally not awaited. Consider optionally logging faults to avoid completely swallowing exceptions in release:

         foreach (var permission in permissions)
         {
-            e.WebView.CoreWebView2.Profile.SetPermissionStateAsync(permission,
+            _ = e.WebView.CoreWebView2.Profile.SetPermissionStateAsync(permission,
                 "https://0.0.0.1",
-                CoreWebView2PermissionState.Allow);
-            e.WebView.CoreWebView2.Profile.SetPermissionStateAsync(permission,
+                CoreWebView2PermissionState.Allow).ContinueWith(t => System.Diagnostics.Debug.WriteLine(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
+            _ = e.WebView.CoreWebView2.Profile.SetPermissionStateAsync(permission,
                 "https://0.0.0.0",
-                CoreWebView2PermissionState.Allow);
+                CoreWebView2PermissionState.Allow).ContinueWith(t => System.Diagnostics.Debug.WriteLine(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
         }
backend/FwLite/FwLiteProjectSync/Import/ResumableImportApi.cs (1)

69-72: Explicit CreateWritingSystem default-parameter removal is safe

I verified that all callers invoke this method via the interface (which still declares between = null), and there are no direct references to the explicit implementation omitting the second argument.

Optional: to avoid any chance of cache-key collision, you could include a delimiter between the type and code. For example, in backend/FwLite/FwLiteProjectSync/Import/ResumableImportApi.cs:71 change:

-    ws => ws.Type + ws.WsId.Code
+    ws => $"{ws.Type}:{ws.WsId.Code}"
backend/FwLite/FwDataMiniLcmBridge/LcmUtils/LcmThreadedProgress.cs (1)

42-45: Canceled/IsCanceling hardcoded to false — consider documenting or wiring a token

If cancellation is intentionally unsupported in this implementation, consider an XML doc comment stating that this progress source never cancels. If limited cancellation becomes desirable later, you could back these by a CancellationTokenSource.

backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs (3)

297-312: CreatePartOfSpeech made sync (Task.FromResult) — LGTM

This removes unnecessary async state machines. Implementation correctly ensures an ID and applies the name.

If you expect these synchronous wrappers to be hot, consider returning ValueTask<T> to avoid Task allocation; otherwise current Task.FromResult is fine for consistency.


359-368: FromLcmPossibility can be expression-bodied (minor)

Tiny readability win; no behavior change.

-private Publication FromLcmPossibility(ICmPossibility lcmPossibility)
-{
-    var possibility = new Publication
-    {
-        Id = lcmPossibility.Guid,
-        Name = FromLcmMultiString(lcmPossibility.Name)
-    };
-
-    return possibility;
-}
+private Publication FromLcmPossibility(ICmPossibility lcmPossibility) =>
+    new Publication
+    {
+        Id = lcmPossibility.Guid,
+        Name = FromLcmMultiString(lcmPossibility.Name)
+    };

346-357: Remove redundant async/await in both CreatePublication methods

Both implementations of CreatePublication use an async modifier only to await Task.FromResult(...). You can simplify these by dropping async/await and returning the Task directly.

Locations:

  • backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs (lines 346–357)
  • backend/FwLite/FwLiteProjectSync/DryRunMiniLcmApi.cs (around line 304)

Suggested changes:

--- a/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
+++ b/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
@@
-    public async Task<Publication> CreatePublication(Publication pub)
+    public Task<Publication> CreatePublication(Publication pub)
     {
         ICmPossibility? lcmPublication = null;
         NonUndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW(Cache.ServiceLocator.ActionHandler, () =>
         {
             lcmPublication = Cache.ServiceLocator.GetInstance<ICmPossibilityFactory>()
                 .Create(pub.Id, Cache.LangProject.LexDbOA.PublicationTypesOA);
             UpdateLcmMultiString(lcmPublication.Name, pub.Name);
         });
-        return await Task.FromResult(FromLcmPossibility(
-            lcmPublication ?? throw new InvalidOperationException("Failed to create publication")));
+        return Task.FromResult(FromLcmPossibility(
+            lcmPublication ?? throw new InvalidOperationException("Failed to create publication")));
     }
--- a/backend/FwLite/FwLiteProjectSync/DryRunMiniLcmApi.cs
+++ b/backend/FwLite/FwLiteProjectSync/DryRunMiniLcmApi.cs
@@
-    public async Task<Publication> CreatePublication(Publication pub)
+    public Task<Publication> CreatePublication(Publication pub)
     {
         DryRunRecords.Add(new DryRunRecord(nameof(CreatePublication), $"Create publication {pub.Id}"));
-        return await Task.FromResult(pub);
+        return Task.FromResult(pub);
     }

By applying these changes, you eliminate unnecessary state-machine overhead and align with the pattern used elsewhere.

📜 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 6b656f2 and 726d8ff.

📒 Files selected for processing (7)
  • backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs (17 hunks)
  • backend/FwLite/FwDataMiniLcmBridge/LcmUtils/LcmThreadedProgress.cs (3 hunks)
  • backend/FwLite/FwLiteMaui/MainPage.xaml.Windows.cs (1 hunks)
  • backend/FwLite/FwLiteProjectSync/Import/ResumableImportApi.cs (1 hunks)
  • backend/FwLite/FwLiteShared/Services/ProjectServicesProvider.cs (2 hunks)
  • backend/LexBoxApi/Controllers/SyncController.cs (1 hunks)
  • backend/Testing/FwHeadless/MediaFileServiceTests.cs (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
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.
📚 Learning: 2025-08-07T08:35:47.803Z
Learnt from: hahn-kev
PR: sillsdev/languageforge-lexbox#1885
File: backend/FwLite/FwLiteMaui/MainPage.xaml.Windows.cs:30-41
Timestamp: 2025-08-07T08:35:47.803Z
Learning: In backend/FwLite/FwLiteMaui/MainPage.xaml.Windows.cs, the SetPermissionStateAsync calls in BlazorWebViewInitialized method are intentionally used in a fire-and-forget pattern with pragma warning suppression for VSTHRD110, as awaiting them is not necessary and async void should be avoided.

Applied to files:

  • backend/FwLite/FwLiteMaui/MainPage.xaml.Windows.cs
📚 Learning: 2025-07-22T09:19:27.281Z
Learnt from: rmunn
PR: sillsdev/languageforge-lexbox#1836
File: backend/FwLite/LcmCrdt/MediaServer/LcmMediaService.cs:120-140
Timestamp: 2025-07-22T09:19:27.281Z
Learning: In the LcmMediaService class (backend/FwLite/LcmCrdt/MediaServer/LcmMediaService.cs), File.OpenWrite should not be used for saving files because it doesn't truncate existing files, which causes data corruption when re-uploading files smaller than the original. Use File.Create instead to ensure proper file truncation.

Applied to files:

  • backend/Testing/FwHeadless/MediaFileServiceTests.cs
🧬 Code graph analysis (2)
backend/FwLite/FwLiteProjectSync/Import/ResumableImportApi.cs (2)
backend/FwLite/FwLiteProjectSync.Tests/Import/ResumableTests.cs (11)
  • Task (139-142)
  • Task (144-147)
  • Task (155-159)
  • Task (161-165)
  • Task (167-174)
  • Task (176-180)
  • Task (181-185)
  • Task (186-190)
  • Task (192-199)
  • Task (201-205)
  • Task (206-210)
backend/FwLite/FwLiteProjectSync/DryRunMiniLcmApi.cs (5)
  • Task (21-26)
  • Task (28-41)
  • Task (43-47)
  • Task (49-53)
  • Task (55-59)
backend/FwLite/FwLiteShared/Services/ProjectServicesProvider.cs (2)
backend/FwLite/MiniLcm/Project/IProjectProvider.cs (1)
  • IEnumerable (8-8)
backend/FwLite/FwLiteShared/Services/MiniLcmJsInvokable.cs (1)
  • Dispose (374-378)
🔇 Additional comments (13)
backend/LexBoxApi/Controllers/SyncController.cs (1)

49-52: LGTM: remove unused exception variable to silence warning without changing behavior

Catching OperationCanceledException without a named variable cleans up the compiler warning and preserves the existing 200 OK Timeout response.

backend/FwLite/FwLiteShared/Services/ProjectServicesProvider.cs (1)

20-21: Constructor signature change verified – no manual instantiations or custom factories detected

All references to ProjectServicesProvider were inspected:

  • DI registration exists in
    • backend/FwLite/FwLiteShared/FwLiteSharedKernel.cs (line 31):
    services.AddScoped<ProjectServicesProvider>();
  • Type mapping in
    • backend/FwLite/FwLiteShared/Services/FwLiteProvider.cs (lines 46, 103):
    DotnetService.ProjectServicesProvider => typeof(ProjectServicesProvider)
  • No occurrences of new ProjectServicesProvider(...) or ActivatorUtilities.CreateInstance/GetService<…ProjectServicesProvider> were found.

Since the logger parameter was dropped from the constructor and DI only ever resolves the concrete type, everything will bind to the updated signature without further changes.

backend/FwLite/FwDataMiniLcmBridge/LcmUtils/LcmThreadedProgress.cs (3)

8-8: Readonly synchronize-invoke field — LGTM

Marking _synchronizeInvoke as readonly is correct and prevents accidental reassignment.


25-25: Expression-bodied SynchronizeInvoke property — LGTM

The expression-bodied member improves clarity with no functional change.


10-12: Confirmed: Canceling event is unused, CS0067 suppression is safe

I searched the entire repository for any subscriptions (.Canceling +=) or invocations (Canceling() of the Canceling event and found no references. Since no code subscribes to or raises this event, the warning suppression can remain without risk of hiding behavioral bugs.

backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs (8)

143-145: Property pattern on LexemeForm — LGTM

Using entry.LexemeFormOA is { Form: not null } is a concise, null-safe guard.


501-516: CreateComplexFormType moved to sync Task.FromResult — LGTM

Implementation correctly adds the created type and returns the projected DTO.


773-783: Sense mapping and collection expressions — LGTM

Collection expressions for SemanticDomains and ExampleSentences are clear and allocation-friendly.

Also applies to: 781-783


1176-1178: Ensure a ComplexForm entryRef exists before adding types — LGTM

This fixes a latent null/empty case by creating a LexEntryRef when none exist. Matches LCM behavior comment.


1427-1437: CreateSense converted to sync Task.FromResult — LGTM

The flow creates and inserts the sense inside a UOW and returns the mapped DTO. Consistent with other Create* refactors.


1572-1583: CreateExampleSentence converted to sync Task.FromResult — LGTM

Same pattern as CreateSense; looks good.


1672-1674: I’ve requested inspection of the PathFromMediaUri implementations and associated tests to confirm whether they ever return absolute paths outside the expected root. Once we have that, we can determine if the suggested hardening is needed.


662-665: Verified: Collection expressions align with List properties in API proxies

I’ve confirmed that in UpdateEntryProxy.cs, the overridden properties are all declared as List<T>:

  • public override List<Sense> Senses
  • public override List<ComplexFormComponent> Components
  • public override List<ComplexFormEntry> ComplexForms
  • public override List<ComplexFormType> ComplexFormTypes

Since collection expressions ([.. entry.AllSenses.Select(...)], etc.) target List<T>, they will compile into new List<T> { … } and match the intended API shape. No further changes needed here.

@myieye myieye merged commit 0f8bce8 into develop Aug 28, 2025
25 checks passed
@myieye myieye deleted the dotnet-warning-format-cleanup branch August 28, 2025 08:06
@hahn-kev hahn-kev removed 📦 Lexbox issues related to any server side code, fw-headless included 💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related labels Sep 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants