-
Notifications
You must be signed in to change notification settings - Fork 0
Fix lyrics JSON deserialization and service lifetimes #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Co-authored-by: punkouter26 <[email protected]>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
| builder.Services.AddSingleton<IDiagnosticValidator, AzureOpenAIDiagnosticValidator>(); | ||
| builder.Services.AddSingleton<IDiagnosticValidator, AzureSpeechDiagnosticValidator>(); | ||
| builder.Services.AddSingleton<IDiagnosticValidator, InternetConnectivityDiagnosticValidator>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Multiple implementations of IDiagnosticValidator are registered as singletons:
builder.Services.AddSingleton<IDiagnosticValidator, AzureOpenAIDiagnosticValidator>();
builder.Services.AddSingleton<IDiagnosticValidator, AzureSpeechDiagnosticValidator>();
builder.Services.AddSingleton<IDiagnosticValidator, InternetConnectivityDiagnosticValidator>();Ensure that all consumers of IDiagnosticValidator are designed to resolve IEnumerable<IDiagnosticValidator>. If any consumer expects a single instance, this will cause runtime errors or unexpected behavior. Review all usages of IDiagnosticValidator to confirm correct resolution.
Recommended solution:
- If multiple implementations are intended, ensure all consumers use
IEnumerable<IDiagnosticValidator>. - If only one implementation should be used, register only the required one.
| /// <summary> | ||
| /// Gets lyrics for a specific song, limited to 200 words. | ||
| /// </summary> | ||
| Task<string?> GetLyricsAsync(string songFileName); | ||
| Task<string?> GetLyricsAsync(string songTitle); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ambiguity in Error Handling
The interface methods GetLyricsAsync and GetRandomLyricsAsync return nullable types, but do not specify how errors (e.g., song not found, IO exceptions) should be handled or propagated. This can lead to inconsistent error handling across implementations. Consider defining a consistent error handling strategy, such as returning a result type that encapsulates both data and error information, or documenting expected exception behavior.
| /// <summary> | ||
| /// Gets lyrics for a specific song, limited to 200 words. | ||
| /// </summary> | ||
| Task<string?> GetLyricsAsync(string songFileName); | ||
| Task<string?> GetLyricsAsync(string songTitle); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unclear Enforcement of 200-Word Limit
The summary for GetLyricsAsync states that lyrics are limited to 200 words, but the interface does not specify how this limit is enforced or whether it is the responsibility of the implementation. This ambiguity may result in inconsistent behavior. It is recommended to clarify in the interface contract whether the limit is enforced by the service or expected from the data source, and to document the expected behavior.
| _lyricsCache = JsonSerializer.Deserialize<LyricsCollection>(jsonContent, _jsonOptions); | ||
|
|
||
| if (_lyricsCache is null) | ||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The deserialization of the lyrics collection does not handle exceptions that may occur if the JSON is malformed or unreadable. This could result in an unhandled exception and application crash.
Recommendation:
Wrap the deserialization in a try-catch block and log the exception, returning a controlled error or null value as appropriate. For example:
try
{
_lyricsCache = JsonSerializer.Deserialize<LyricsCollection>(jsonContent, _jsonOptions);
}
catch (JsonException ex)
{
_logger.LogError(ex, "Failed to deserialize lyrics collection JSON");
throw new InvalidOperationException("Failed to deserialize lyrics collection", ex);
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes lyrics loading by aligning JSON deserialization with the lyrics-collection.json schema, updates the lyrics API integration test to hit the correct titles endpoint, and changes several DI registrations to Singleton to support caching.
Changes:
- Fix lyrics JSON deserialization (map
content→Lyrics, enable case-insensitive property matching). - Adjust lyrics lookup to use
songTitledirectly (avoid stripping dotted titles). - Update integration test endpoint and change multiple service lifetimes to Singleton; update
global.jsonSDK pin.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Po.VicTranslate.IntegrationTests/LyricsEndpointTests.cs | Updates the integration test to call /api/lyrics/titles. |
| src/PoVicTranslate.Web/Services/LyricsService.cs | Uses case-insensitive JSON options and changes lookup to compare by songTitle. |
| src/PoVicTranslate.Web/Services/ILyricsService.cs | Renames the API parameter from songFileName to songTitle. |
| src/PoVicTranslate.Web/Program.cs | Switches several service registrations from Scoped to Singleton. |
| src/PoVicTranslate.Web/Models/LyricsCollection.cs | Maps JSON content field onto SongEntry.Lyrics. |
| global.json | Pins the repo SDK version to 10.0.100. |
| "version": "10.0.100", | ||
| "rollForward": "latestPatch", | ||
| "allowPrerelease": false |
Copilot
AI
Jan 31, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
global.json now pins the SDK to 10.0.100 with rollForward: latestPatch. latestPatch will not roll forward across SDK feature bands (e.g., 10.0.100 -> 10.0.102), so this can break builds on machines/CI that only have 10.0.102 installed (your GitHub Actions workflow installs 10.0.x). Consider either pinning to the feature band used in CI (e.g., 10.0.102) or changing rollForward to latestFeature/latestMinor, or updating CI to install 10.0.100 explicitly.
| // Register validation services | ||
| builder.Services.AddSingleton<ISpeechConfigValidator, SpeechConfigValidator>(); | ||
| builder.Services.AddScoped<IAudioSynthesisService, AudioSynthesisService>(); | ||
| builder.Services.AddSingleton<IAudioSynthesisService, AudioSynthesisService>(); |
Copilot
AI
Jan 31, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switching AudioSynthesisService to Singleton introduces shared mutable state across requests (_accessToken/_tokenExpiry in AudioSynthesisService). As written, token refresh is not synchronized, so concurrent calls can race and trigger multiple token fetches (or transiently observe stale values). Either keep this service scoped, or add synchronization around token refresh (e.g., a lock/SemaphoreSlim) or move token caching into IMemoryCache with proper concurrency control.
| builder.Services.AddSingleton<IAudioSynthesisService, AudioSynthesisService>(); | |
| builder.Services.AddScoped<IAudioSynthesisService, AudioSynthesisService>(); |
| var jsonContent = await File.ReadAllTextAsync(_lyricsFilePath); | ||
| _lyricsCache = JsonSerializer.Deserialize<LyricsCollection>(jsonContent); | ||
| _lyricsCache = JsonSerializer.Deserialize<LyricsCollection>(jsonContent, _jsonOptions); | ||
|
|
Copilot
AI
Jan 31, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR fixes JSON deserialization and changes how lyrics are looked up by songTitle, but the integration tests still don’t cover the successful path of /api/lyrics/{songTitle} returning non-empty lyrics (and the 200-word truncation behavior). Adding a test that fetches /api/lyrics/titles, picks a title, then calls /api/lyrics/{title} and asserts a non-empty Lyrics field (and optionally word-count <= 200) would prevent regressions.
Fixed several issues in the codebase:
LyricsServiceto correctly deserialize JSON by adding[JsonPropertyName("content")]toSongEntryand enabling case-insensitive property matching.LyricsServiceand other backend services to Singleton lifetime to enable caching and optimize resource usage.LyricsService.GetLyricsAsyncwhich caused issues with song titles containing dots.GetAvailableSongs_ShouldReturnListto use the correct API endpoint/api/lyrics/titles.global.jsonto match the installed .NET SDK version.PR created automatically by Jules for task 8833489810780996995 started by @punkouter26