Skip to content

Refactor SourceType handling to enum#6313

Open
Trenly wants to merge 5 commits into
microsoft:masterfrom
Trenly:trenly/source-type-enum-4463
Open

Refactor SourceType handling to enum#6313
Trenly wants to merge 5 commits into
microsoft:masterfrom
Trenly:trenly/source-type-enum-4463

Conversation

@Trenly

@Trenly Trenly commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

📖 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:

  • SourceDetails.Type and internal source factory routing now use SourceType.
  • ISourceFactory::GetForType now takes SourceType.
  • Source constructor string-type overload removal and default-type string helper removal at call sites.
  • Source parsing/serialization boundaries explicitly convert using TryConvertToSourceTypeEnum/SourceTypeEnumToString.
  • Source-related tests updated for enum semantics, including legacy tombstone/override source-list parsing compatibility.

Copilot assisted with implementation.

🔗 References

Resolves #4463

🔍 Validation

  • Executed source-focused test subset:
    • ./src/x64/Debug/AppInstallerCLITests/AppInstallerCLITests.exe -v high -d yes 'Source'

✅ Checklist

📋 Issue Type

  • Bug fix
  • Feature
  • Task
Microsoft Reviewers: Open in CodeFlow

Trenly and others added 4 commits June 21, 2026 16:08
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>
@Trenly Trenly marked this pull request as ready for review June 21, 2026 21:30
@Trenly Trenly requested a review from a team as a code owner June 21, 2026 21:30

@florelis florelis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Comment thread doc/ReleaseNotes.md

## Bug Fixes

* Refactored source type handling to use a `SourceType` enum with centralized string conversion, and added stronger `source add` type validation (#4463).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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?"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fair - 🤖 was just following default instructions, will remove

PredefinedInstalled,
PredefinedWriteable,
PackageTracking,
ConfigurableTest,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this be gated by the test hooks being enabled?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: Is this the naming scheme we usually follow for these enum<->string conversions? I see the one below is just ToString

Comment on lines +143 to +144
// 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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there any reason we'd need this historical note?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Probably not - but I thought it might help a review

Comment on lines +75 to +79
// 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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: Do we need both? Do we usually have both?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Remove outdated comment

return Microsoft::ConfigurableTestSourceFactory::Create();
}
#else
case SourceType::ConfigurableTest:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Duplicated

Comment on lines +969 to +970
// 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 << "]");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure what this does, but the logic changed. If you get the 'preindexed package' as input, you will execute this conditional.

@Trenly Trenly Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

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.

Move SourceType to an Enum

2 participants