Skip to content
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

(GH-431) Add Count to List Command API #430

Merged
merged 3 commits into from
Oct 7, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 deletions src/chocolatey/GetChocolatey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,36 @@ public void RunConsole(string[] args)
runner.run(args, configuration, _container);
}

/// <summary>
/// Run chocolatey after setting the options, and list the results.
/// </summary>
/// <typeparam name="T">The typer of results you're expecting back.</typeparam>
public IEnumerable<T> List<T>()
{
extract_resources();
var configuration = create_configuration(new List<string>());
configuration.RegularOutput = true;
var runner = new GenericRunner();
return runner.list<T>(configuration, _container, isConsole: false, parseArgs: null);
}

/// <summary>
/// Run chocolatey after setting the options,
/// and get the count of items that would be returned if you listed the results.
/// </summary>
/// <remarks>
/// Is intended to be more efficient then simply calling <see cref="List{T}">List</see> and then Count() on the returned list.
/// It also returns the full count as is ignores paging.
/// </remarks>
public int ListCount()
{
extract_resources();
var configuration = create_configuration(new List<string>());
configuration.RegularOutput = true;
var runner = new GenericRunner();
return runner.count(configuration, _container, isConsole: false, parseArgs: null);
}

private ChocolateyConfiguration create_configuration(IList<string> args)
{
var configuration = new ChocolateyConfiguration();
Expand Down Expand Up @@ -213,15 +243,6 @@ private void extract_resources()

AssemblyFileExtractor.extract_all_resources_to_relative_directory(_container.GetInstance<IFileSystem>(), Assembly.GetAssembly(typeof(ChocolateyResourcesAssembly)), ApplicationParameters.InstallLocation, folders, ApplicationParameters.ChocolateyFileResources);
}

public IEnumerable<T> List<T>()
{
extract_resources();
var configuration = create_configuration(new List<string>());
configuration.RegularOutput = true;
var runner = new GenericRunner();
return runner.list<T>(configuration, _container, isConsole: false, parseArgs: null);
}
}

// ReSharper restore InconsistentNaming
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ public IEnumerable<PackageResult> list(ChocolateyConfiguration configuration)
return _packageService.list_run(configuration);
}

public int count(ChocolateyConfiguration config)
{
config.QuietOutput = true;
return _packageService.count_run(config);
}

public bool may_require_admin_access()
{
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ public IEnumerable<ChocolateySource> list(ChocolateyConfiguration configuration)
return _configSettingsService.source_list(configuration);
}

public int count(ChocolateyConfiguration config)
{
return list(config).Count();
}

public bool may_require_admin_access()
{
return true;
Expand Down
57 changes: 50 additions & 7 deletions src/chocolatey/infrastructure.app/nuget/NugetList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace chocolatey.infrastructure.app.nuget
{
using System.Collections.Generic;
using System.Linq;

using NuGet;
using configuration;

Expand All @@ -25,13 +26,47 @@ namespace chocolatey.infrastructure.app.nuget
public static class NugetList
{
public static IEnumerable<IPackage> GetPackages(ChocolateyConfiguration configuration, ILogger nugetLogger)
{
return execute_package_search(configuration, nugetLogger);
}

public static int GetCount(ChocolateyConfiguration configuration, ILogger nugetLogger)
{
return execute_package_search(configuration, nugetLogger).Count();
}

private static IQueryable<IPackage> execute_package_search(ChocolateyConfiguration configuration, ILogger nugetLogger)
{
var packageRepository = NugetCommon.GetRemoteRepository(configuration, nugetLogger);

// Whether or not the package is remote determines two things:
// 1. Does the repository have a notion of "listed"?
// 2. Does it support prerelease in a straight-forward way?
// Choco previously dealt with this by taking the path of least resistance and manually filtering out and sort unwanted packages
// This result in blocking operations that didn't let service based repositories, like OData, take care of heavy lifting on the server.
bool isRemote;
var aggregateRepo = packageRepository as AggregateRepository;
if (aggregateRepo != null)
{
isRemote = aggregateRepo.Repositories.All(repo => repo is IServiceBasedRepository);
}
else
{
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Both the above checks were completely superfluous new as I can tell. The former was redundant with checks the server is already doing, and the later is redundant with the IsPrelease check.

Copy link
Member

Choose a reason for hiding this comment

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

Please ensure you are testing against multiple NuGet source types.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you have any other recommended NuGet sources I should test against besides Chocolatey?

Copy link
Member

Choose a reason for hiding this comment

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

  • File source - which you get with list -lo
  • NuGet.org itself
  • And maybe a myget source if you can get your hands on one.

Earlier when @Jaykul was working on some of this that was similar I could not get the results to match. It wasn't necessarily the count, although it was off as well. It was the order coming back from the query, it was off. #224 (comment) and #224 (comment)

Copy link
Member

Choose a reason for hiding this comment

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

What I'd really love to see before someone starts changing these core bits is some integration tests that really test this out (prior to making the changes). Then make the changes and see if everything is still kosher.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Absolutely agree. Something I was thinking about. Just have to figure out how to reliable intregration test this :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Earlier when @Jaykul was working on some of this that was similar I could not get the results to match. It wasn't necessarily the count, although it was off as well. It was the order coming back from the query, it was off. #224 (comment) and #224 (comment)

Reviewing them, I'm going to run into the exact same issue, but I'm also unwilling to add distinct_last back. I'd much rather find a way to detect whether if the underlying provider is sourcing from a server than make Search a blocking operation.

isRemote = packageRepository is IServiceBasedRepository;
}

IQueryable<IPackage> results = packageRepository.Search(configuration.Input, configuration.Prerelease);

if (configuration.AllVersions)
{
return results.Where(PackageExtensions.IsListed).OrderBy(p => p.Id);
if (isRemote)
{
return results.OrderBy(p => p.Id);
}
else
{
return results.Where(PackageExtensions.IsListed).OrderBy(p => p.Id).AsQueryable();
}
}

if (configuration.Prerelease && packageRepository.SupportsPrereleasePackages)
Expand All @@ -43,17 +78,25 @@ public static IEnumerable<IPackage> GetPackages(ChocolateyConfiguration configur
results = results.Where(p => p.IsLatestVersion);
}

if (!isRemote)
{
results =
results
.Where(PackageExtensions.IsListed)
.Where(p => configuration.Prerelease || p.IsReleaseVersion())
.distinct_last(PackageEqualityComparer.Id, PackageComparer.Version)
.AsQueryable();
}

Copy link
Member

Choose a reason for hiding this comment

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

I can live with this :)

if (configuration.ListCommand.Page.HasValue)
{
results = results.Skip(configuration.ListCommand.PageSize * configuration.ListCommand.Page.Value).Take(configuration.ListCommand.PageSize);
}

return results.OrderBy(p => p.Id)
.AsEnumerable()
.Where(PackageExtensions.IsListed)
.Where(p => configuration.Prerelease || p.IsReleaseVersion())
.distinct_last(PackageEqualityComparer.Id, PackageComparer.Version);
}
return results.OrderBy(p => p.Id);
}


}

// ReSharper restore InconsistentNaming
Expand Down
18 changes: 18 additions & 0 deletions src/chocolatey/infrastructure.app/runners/GenericRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,24 @@ public IEnumerable<T> list<T>(ChocolateyConfiguration config, Container containe
}
}

public int count(ChocolateyConfiguration config, Container container, bool isConsole, Action<ICommand> parseArgs)
{
var command = find_command(config, container, isConsole, parseArgs) as IListCommand;
if (command == null)
{
if (!string.IsNullOrWhiteSpace(config.CommandName))
{
throw new Exception("The implementation of '{0}' does not support listing.".format_with(config.CommandName));
}
return 0;
}
else
{
this.Log().Debug("_ {0}:{1} - Normal Count Mode _".format_with(ApplicationParameters.Name, command.GetType().Name));
return command.count(config);
}
}

public void warn_when_admin_needs_elevation(ChocolateyConfiguration config)
{
var shouldWarn = (!config.Information.IsProcessElevated && config.Information.IsUserAdministrator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public void ensure_source_app_installed(ChocolateyConfiguration config)
perform_source_runner_action(config, r => r.ensure_source_app_installed(config, (packageResult) => handle_package_result(packageResult, config, CommandNameType.install)));
}

public int count_run(ChocolateyConfiguration config)
{
return perform_source_runner_function(config, r => r.count_run(config));
}

private void perform_source_runner_action(ChocolateyConfiguration config, Action<ISourceRunner> action)
{
var runner = _sourceRunners.FirstOrDefault(r => r.SourceType == config.SourceType);
Expand Down
5 changes: 5 additions & 0 deletions src/chocolatey/infrastructure.app/services/CygwinService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ public void ensure_source_app_installed(ChocolateyConfiguration config, Action<P
set_root_dir_if_not_set();
}

public int count_run(ChocolateyConfiguration config)
{
throw new NotImplementedException("Count is not supported for this source runner.");
}

public void set_root_dir_if_not_set()
{
if (!string.IsNullOrWhiteSpace(_rootDirectory)) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ public interface IChocolateyPackageService
/// <param name="config">The configuration.</param>
void ensure_source_app_installed(ChocolateyConfiguration config);

/// <summary>
/// Retrieves the count of items that meet the search criteria.
/// </summary>
/// <param name="config"></param>
/// <returns></returns>
int count_run(ChocolateyConfiguration config);

/// <summary>
/// Run list in noop mode
/// </summary>
Expand Down
7 changes: 7 additions & 0 deletions src/chocolatey/infrastructure.app/services/ISourceRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ public interface ISourceRunner
/// <param name="ensureAction">The action to continue with as part of the install</param>
void ensure_source_app_installed(ChocolateyConfiguration config, Action<PackageResult> ensureAction);

/// <summary>
/// Retrieve the listed packages from the source feed cout
/// </summary>
/// <param name="config">The configuration.</param>
/// <returns>Packages count</returns>
int count_run(ChocolateyConfiguration config);

/// <summary>
/// Run list in noop mode
/// </summary>
Expand Down
21 changes: 21 additions & 0 deletions src/chocolatey/infrastructure.app/services/NugetService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,27 @@ public void ensure_source_app_installed(ChocolateyConfiguration config, Action<P
// nothing to do. Nuget.Core is already part of Chocolatey
}

public int count_run(ChocolateyConfiguration config)
{
int count = 0;

if (config.ListCommand.LocalOnly)
{
config.Sources = ApplicationParameters.PackagesLocation;
config.Prerelease = true;
}

int? pageValue = config.ListCommand.Page;
try
{
return NugetList.GetCount(config, _nugetLogger);
}
finally
{
config.ListCommand.Page = pageValue;
}
}

public void list_noop(ChocolateyConfiguration config)
{
this.Log().Info("{0} would have searched for '{1}' against the following source(s) :\"{2}\"".format_with(
Expand Down
5 changes: 5 additions & 0 deletions src/chocolatey/infrastructure.app/services/PythonService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,11 @@ public void ensure_source_app_installed(ChocolateyConfiguration config, Action<P
}
}

public int count_run(ChocolateyConfiguration config)
{
throw new NotImplementedException("Count is not supported for this source runner.");
}

public void set_executable_path_if_not_set()
{
if (!string.IsNullOrWhiteSpace(_exePath)) return;
Expand Down
5 changes: 5 additions & 0 deletions src/chocolatey/infrastructure.app/services/RubyGemsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ public void ensure_source_app_installed(ChocolateyConfiguration config, Action<P
}
}

public int count_run(ChocolateyConfiguration config)
{
throw new NotImplementedException("Count is not supported for this source runner.");
}

public void list_noop(ChocolateyConfiguration config)
{
var args = ExternalCommandArgsBuilder.build_arguments(config, _listArguments);
Expand Down
5 changes: 5 additions & 0 deletions src/chocolatey/infrastructure.app/services/WebPiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ public void ensure_source_app_installed(ChocolateyConfiguration config, Action<P
}
}

public int count_run(ChocolateyConfiguration config)
{
throw new NotImplementedException("Count is not supported for this source runner.");
}

public void list_noop(ChocolateyConfiguration config)
{
var args = ExternalCommandArgsBuilder.build_arguments(config, _listArguments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ public void ensure_source_app_installed(ChocolateyConfiguration config, Action<P
set_executable_path_if_not_set();
}

public int count_run(ChocolateyConfiguration config)
{
throw new NotImplementedException("Count is not supported for this source runner.");
}

public void set_executable_path_if_not_set()
{
if (!string.IsNullOrWhiteSpace(_exePath)) return;
Expand Down
7 changes: 6 additions & 1 deletion src/chocolatey/infrastructure/commands/IListCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ namespace chocolatey.infrastructure.commands
using System.Collections.Generic;
using app.configuration;

public interface IListCommand<out T> : ICommand
public interface IListCommand : ICommand
{
int count(ChocolateyConfiguration config);
}

public interface IListCommand<out T> : IListCommand
{
IEnumerable<T> list(ChocolateyConfiguration config);
}
Expand Down