-
Notifications
You must be signed in to change notification settings - Fork 17
CSV EPIC #149
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
CSV EPIC #149
Conversation
- Convert CSVDeliverer and CSVDiscoverer to primary constructors - Add XML documentation with CREF references for CSV classes - Update variable names for consistency - Format code and organize using statements - Create CSVConstants.cs file and update CSV files to use constants
Skyaero42
left a comment
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.
This needs another pass
- Adhere to the single responsibility principle (SRP) use private methods for small tasks, rather than putting everything in one big method.
- Consider using private/internal data objects rather than a bunch of variables if they are related. This also allows validation logic to be moved to the object.
- Consider using a CSV reading library.
- There is hardcoded stuff for generals 1.08 and zero hour 1.04. This is a major issue as these versions are only available when users have applied genpatcher. The official latest versions are 1.05 and 1.09. There is also Generals Online and the SuperHackers pre-release. This needs a redesign.
| { | ||
| _logger.LogError(CsvConstants.CsvUrlMissingLog, discoveredItem?.Id); | ||
| return OperationResult<ContentManifest>.CreateFailure(CsvConstants.CsvUrlNotProvidedError); | ||
| } |
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.
(line 75-124): Obtaining the csvUrl should be a separate (private) function to adhere to the single responsibility principle
| } | ||
|
|
||
| // Determine requested target game (if the discoverer set it) | ||
| var targetGame = discoveredItem.TargetGame != default ? discoveredItem.TargetGame : (GameType?)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.
Replacing var for GameType? and removing the cast from null increases readability
| catch | ||
| { | ||
| // Ignore | ||
| } |
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.
Getting the targetGame should be its own method to adhere to SRP.
| { | ||
| _logger.LogWarning(CsvConstants.SizeParseFailedWarning, relativePath, lineIndex); | ||
| continue; | ||
| } |
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.
Download and parsing the CSV should be a method or methods by it/themselves to adhere to SRP.
| using var reader = new StreamReader(stream); | ||
|
|
||
| // Parse CSV streaming | ||
| var files = new List<ManifestFile>(); |
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.
Why not use a parser library?
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.
^ I agree with this one, I already mentioned CsvHelper in another comment.
GenHub/GenHub/Features/Content/Services/ContentDiscoverers/CSVDiscoverer.cs
Outdated
Show resolved
Hide resolved
|
|
||
| var fileValid = true; | ||
|
|
||
| // Check MD5 |
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.
create private method
| } | ||
| } | ||
|
|
||
| // Check SHA256 |
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.
create private method
| continue; | ||
| } | ||
|
|
||
| var relativePath = columns[CsvConstants.RelativePathColumnIndex].Trim(); |
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.
Consider using an object
| CurrentOperation = CsvConstants.DownloadingCsvMessage, | ||
| }); | ||
|
|
||
| // CSV download |
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.
separate method
|
Additionally, create a clear title what this PR does, add a (short) description what it does and link the relevant issues ( |
| namespace GenHub.Core.Constants; | ||
|
|
||
| /// <summary>Constants used for CSV file processing and content handling.</summary> | ||
| public static class CsvConstants |
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.
Why don't you just use CsvHelper? It takes care of so much of the CSV work for you, then you don't have to remember what the delimiter is, and if the Language is in column 5, etc. You just read the CSV into an object and you're done. Extremely quick & easy, and also much easier to read code-wise & work with from that point forward.
| public const string FileSystemSourceName = "FileSystem"; | ||
|
|
||
| /// <summary>Default hash algorithm to use for file validation.</summary> | ||
| public const string DefaultHashAlgorithm = "SHA256"; |
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.
I'm pretty sure this one is already in a constant somewhere.
| public const string DefaultHashAlgorithm = "SHA256"; | ||
|
|
||
| /// <summary>Progress message for CSV download phase.</summary> | ||
| public const string DownloadingCsvMessage = "Downloading CSV file..."; |
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.
Personally I think if you're only using this message in a log for example, then you really don't have to turn it into a Constant. I think this is overusing constants a little bit.
| try | ||
| { | ||
| // Some ContentSearchResult implementations expose a dictionary property named ResolverMetadata | ||
| // We'll attempt to read it via dynamic/Reflection style if typed property isn't available. |
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.
So, is the typed property available? It says "if" it's available.
And is there no easier way of doing this than to use reflection? I feel like often it's needlessly complex and reflection isn't very performant.
| using var reader = new StreamReader(stream); | ||
|
|
||
| // Parse CSV streaming | ||
| var files = new List<ManifestFile>(); |
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.
^ I agree with this one, I already mentioned CsvHelper in another comment.
| { | ||
| private readonly ILogger<CSVDiscoverer> _logger = logger; | ||
| private readonly ManifestDiscoveryService _manifestDiscoveryService = manifestDiscoveryService; | ||
| private readonly IConfigurationProviderService _configurationProvider = configurationProvider; |
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.
Maybe he likes to have all names with underscores, which is not possible directly in the primary constructor. Maybe you can just circumvent this by keeping these lines and adding ?? throw new ArgumentNullException(...);
GenHub/GenHub/Features/Content/Services/ContentDiscoverers/CSVDiscoverer.cs
Outdated
Show resolved
Hide resolved
|
|
||
| // This class implemented in a very basic way and needs more work | ||
| // It needs to be split into smaller methods | ||
| // Also, the CSV format should be more robustly defined (e.g. using a library like CsvHelper) |
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.
Hey, I'm glad to see you mentioning this yourself! That's makes me happy actually, nice job. I'm curious to see what you come up with.
Maybe for next time though: if something needs more work, consider marking the PR as 'draft' until you're happy with it and have addressed all or most of your own bullet points. Until I saw this section, I was under the impression that this PR is "ready to go", but now I think otherwise and feel like you're saying the same thing here. :)
| return OperationResult<ContentManifest>.CreateFailure($"{CsvConstants.ContentNotFoundError}: {contentId}"); | ||
| } | ||
|
|
||
| var result = searchResult.Data!.First(); |
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.
This code is unsafe. Why not write it like this:
var result = searchResult?.Data?.FirstOrDefault();
var manifest = result?.GetData<ContentManifest>();
return manifest != null
? OperationResult<ContentManifest>.CreateSuccess(manifest)
: OperationResult<ContentManifest>.CreateFailure(CsvConstants.ManifestNotAvailableError);
This way an exception will be avoided in every single case a variable here could be null.
| var searchResult = await SearchAsync(query, cancellationToken); | ||
| if (!searchResult.Success || !searchResult.Data!.Any()) | ||
| { | ||
| return OperationResult<ContentManifest>.CreateFailure($"{CsvConstants.ContentNotFoundError}: {contentId}"); |
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.
Why not use the Result.Message or whatever it's called in the response?
…idation fallback - Add CSVDiscoverer and CSVResolver for querying and parsing CSV catalogs - Introduce CsvCatalogEntry model and registry index support for metadata - Enhance GameInstallationValidator with TryCsvValidationAsync fallback - Update ContentSearchQuery with language normalization for CSV filtering - Add LanguageDetector for installation language detection - Integrate CSV components into DI modules (ValidationModule, ContentPipelineModule) - Create comprehensive unit tests for CSV models, discoverer, resolver, and detector - Generate initial CSV files (Generals-1.08.csv, ZeroHour-1.04.csv) and index.json - Add CSV configuration keys and constants for production use Resolves multiple open issues in CSV PR #XX including discovery filtering, language support, manifest generation, and integration with existing validation pipeline.
…ase game manifests for Generals and Zero Hour
…ase game manifests for Generals and Zero Hour (#180)
No description provided.