Refactor SourceType handling to enum#6313
Conversation
Migrate SourceDetails and source factory internals to use SourceType enum paths while keeping string conversion at input/output boundaries. Align CLI/COM/repository flows and source-related tests with canonical enum conversion behavior, and keep legacy user source tombstone/override parsing behavior intact. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
florelis
left a comment
There was a problem hiding this comment.
I haven't finished reviewing. My main comment so far is: Why move where in the code empty types are handled? We could have kept it the same, and then you wouldn't have as many logic changes to verify.
|
|
||
| ## Bug Fixes | ||
|
|
||
| * Refactored source type handling to use a `SourceType` enum with centralized string conversion, and added stronger `source add` type validation (#4463). |
There was a problem hiding this comment.
I'm not sure we need to mention this here. An internal refactor doesn't seem too relevant to someone asking "what's new in this release?"
There was a problem hiding this comment.
Fair - 🤖 was just following default instructions, will remove
| PredefinedInstalled, | ||
| PredefinedWriteable, | ||
| PackageTracking, | ||
| ConfigurableTest, |
There was a problem hiding this comment.
Should this be gated by the test hooks being enabled?
There was a problem hiding this comment.
I opted to leave the enum alone and have the test hooks handle the implementation / backend so that enum indexes don't change between debug / test hook builds and release builds
| std::optional<SourceType> TryConvertToSourceTypeEnum(std::string_view sourceType); | ||
|
|
||
| // Converts a SourceType enum to the corresponding canonical string. | ||
| std::string_view SourceTypeEnumToString(SourceType sourceType); |
There was a problem hiding this comment.
Nit: Is this the naming scheme we usually follow for these enum<->string conversions? I see the one below is just ToString
| // Defaults to PreIndexedPackage if not specified, moved here from call sites during migration from | ||
| // String type to Enum type to avoid conversion in multiple places. |
There was a problem hiding this comment.
Is there any reason we'd need this historical note?
There was a problem hiding this comment.
Probably not - but I thought it might help a review
| // Converts a source type string to the corresponding SourceType enum. | ||
| SourceType ConvertToSourceTypeEnum(std::string_view sourceType); | ||
|
|
||
| // Attempts to convert a source type string to the corresponding SourceType enum. | ||
| std::optional<SourceType> TryConvertToSourceTypeEnum(std::string_view sourceType); |
There was a problem hiding this comment.
Nit: Do we need both? Do we usually have both?
There was a problem hiding this comment.
We usually don't have both, and instead have <Enum>::Unknown as the fallback. The reason for having both here was to avoid needing the Unknown type in the enum
| std::unique_ptr<ISourceFactory> ISourceFactory::GetForType(SourceType type) | ||
| { | ||
| #ifndef AICLI_DISABLE_TEST_HOOKS | ||
| // Tests can ensure case matching |
| return Microsoft::ConfigurableTestSourceFactory::Create(); | ||
| } | ||
| #else | ||
| case SourceType::ConfigurableTest: |
| // If the source type was empty, the constructor of sourceDetails will have defaulted it | ||
| AICLI_LOG(Repo, Info, << "Adding source: Name[" << sourceDetails.Name << "], Type[" << SourceTypeEnumToString(sourceDetails.Type) << "], Arg[" << sourceDetails.Arg << "]"); |
There was a problem hiding this comment.
I wonder if it would be worth it to keep the semantics of "type can be empty and that means default" to simplify the changes in logic for this PR. I'm not sure how much we win changing the meaning now, and whether we may be missing some place.
There was a problem hiding this comment.
That's actually why I added the comment - because I wanted it to be clear that by movng to a strongly typed enum here we would be losing some of that nuance of "Type can be empty"
I'm sure that I could find a way to keep that nuance, but semantically I'd rather always have an explicit type, that way there isn't any potential confusion about what empty actually means. If empty is passed into the constructor it gets defaulted, and IMO that makes all the downstream logic simpler because callsites don't have to worry whether it is empty or not, it becomes a case they don't need to worry about.
| if (sourceType.empty() | ||
| || Utility::CaseInsensitiveEquals( Repository::Microsoft::PredefinedInstalledSourceFactory::Type(), sourceType)) | ||
| auto sourceTypeEnum = ::AppInstaller::Repository::TryConvertToSourceTypeEnum(sourceType).value_or(::AppInstaller::Repository::Source::GetDefaultSourceType()); | ||
| if (sourceTypeEnum == ::AppInstaller::Repository::SourceType::PreIndexedPackage || sourceTypeEnum == ::AppInstaller::Repository::SourceType::PredefinedInstalled) |
There was a problem hiding this comment.
I'm not sure what this does, but the logic changed. If you get the 'preindexed package' as input, you will execute this conditional.
There was a problem hiding this comment.
This is an artifact of not allowing an empty source type. If sourceType were empty in this new flow, TryConvertToSourceTypeEnum is an empty optional and, according to the old logic, means 'default'. This is handled by the .value_or setting it to the default.
Then, because the source type could be default or predefined installed, I check both.
In either case, I believe that the PreIndexed package is intended to follow into this conditional as it is setting the weights for the progress bar which depend on downloading and installing the index, which are both part of the pre-indexed package flow. Excluding it here would cause it to not have the same behavior
📖 Description
This refactor moves internal source-type handling to Repository::SourceType enum paths across repository, CLI, and COM layers, while preserving string conversion only at external boundaries (CLI/JSON/GP/COM input-output). It also removes accidental string-based source construction paths and updates source-related tests/fixtures to use canonical source type values.
Key changes include:
Copilot assisted with implementation.
🔗 References
Resolves #4463
🔍 Validation
✅ Checklist
📋 Issue Type
Microsoft Reviewers: Open in CodeFlow