Minor refactoring, add enumeration of storage container by folder, by container, or by entire account

This commit is contained in:
Yaakov 2021-09-26 00:33:10 +10:00
parent a4f9f13780
commit 4743b1a74c
4 changed files with 123 additions and 43 deletions

View file

@ -0,0 +1,40 @@
using System;
using System.Collections.Immutable;
using System.Threading.Tasks;
using CommandLine;
using CommandLine.Text;
using Microsoft.Extensions.DependencyInjection;
namespace Shamir.Console
{
public abstract class ParsedArgumentsCommand<TOptions> : ICommand
{
public abstract string Name { get; }
public abstract string Description { get; }
public ImmutableArray<string> Arguments { get; private set; }
public void Initialize(ReadOnlySpan<string> args)
{
Arguments = args.ToImmutableArray();
}
public async ValueTask<int> ExecuteAsync(IServiceProvider serviceProvider)
{
using var parser = serviceProvider.GetRequiredService<Parser>();
var result = parser.ParseArguments<TOptions>(Arguments);
return await result.MapResult(
options => ExecuteAsync(serviceProvider, options),
errors =>
{
var helpText = HelpText.AutoBuild(result);
helpText.Heading = string.Empty;
helpText.Copyright = string.Empty;
System.Console.Error.WriteLine(helpText);
return ValueTask.FromResult(1);
}
);
}
public abstract ValueTask<int> ExecuteAsync(IServiceProvider serviceProvider, TOptions options);
}
}

View file

@ -1,48 +1,20 @@
using System; using System;
using System.Collections.Immutable;
using System.Threading.Tasks; using System.Threading.Tasks;
using Azure.Storage.Blobs; using Azure.Storage.Blobs;
using CommandLine; using CommandLine;
using Microsoft.Extensions.DependencyInjection;
namespace Shamir.Console namespace Shamir.Console
{ {
public abstract class ParsedArgumentsCommand<TOptions> : ICommand
{
public abstract string Name { get; }
public abstract string Description { get; }
public ImmutableArray<string> Arguments { get; private set; }
public void Initialize(ReadOnlySpan<string> args)
{
var array = ImmutableArray.CreateBuilder<string>(args.Length);
foreach (var arg in args)
{
array.Add(arg);
}
Arguments = array.MoveToImmutable();
}
public async ValueTask<int> ExecuteAsync(IServiceProvider serviceProvider)
{
using var parser = serviceProvider.GetRequiredService<Parser>();
var result = parser.ParseArguments<TOptions>(Arguments);
return await result.MapResult(
options => ExecuteAsync(serviceProvider, options),
errors => ValueTask.FromResult(1)
);
}
public abstract ValueTask<int> ExecuteAsync(IServiceProvider serviceProvider, TOptions options);
}
public class StorageLsOptions public class StorageLsOptions
{ {
[Option("connection-string", Required = false)] [Option("connection-string", Required = false, HelpText = "Azure Storage connection string for the Storage Account backing the CDN.")]
public string? ConnectionString { get; set; } public string? ConnectionString { get; set; }
[Value(0)] [Option('a', "all", HelpText = "List all blobs in the container")]
public string? ContainerName { get; set; } public bool EnumerateAll { get; set; }
[Value(0, MetaName = "Path", HelpText = "Path to enumerate, starting with the Azure Storage container name.")]
public string? Path { get; set; }
} }
public sealed class StorageLsCommand : ParsedArgumentsCommand<StorageLsOptions> public sealed class StorageLsCommand : ParsedArgumentsCommand<StorageLsOptions>
@ -55,23 +27,68 @@ namespace Shamir.Console
{ {
var connectionString = options.ConnectionString ?? Environment.GetEnvironmentVariable("AZURE_CONNECTION_STRING"); var connectionString = options.ConnectionString ?? Environment.GetEnvironmentVariable("AZURE_CONNECTION_STRING");
if (options.ContainerName is null) if (options.Path is null)
{ {
var client = new BlobServiceClient(connectionString); if (options.EnumerateAll)
await foreach (var container in client.GetBlobContainersAsync())
{ {
System.Console.WriteLine(container.Name); var client = new BlobServiceClient(connectionString);
await foreach (var container in client.GetBlobContainersAsync())
{
var containerClient = new BlobContainerClient(connectionString, container.Name);
await foreach (var blob in containerClient.GetBlobsAsync())
{
System.Console.Write(container.Name);
System.Console.Write('/');
System.Console.WriteLine(blob.Name);
}
}
}
else
{
var client = new BlobServiceClient(connectionString);
await foreach (var container in client.GetBlobContainersAsync())
{
System.Console.WriteLine(container.Name);
}
}
}
else if (options.EnumerateAll)
{
var containerName = options.Path;
var client = new BlobContainerClient(connectionString, containerName);
await foreach (var blob in client.GetBlobsAsync())
{
System.Console.Write(containerName);
System.Console.Write('/');
System.Console.WriteLine(blob.Name);
} }
} }
else else
{ {
var client = new BlobContainerClient(connectionString, options.ContainerName); var delimiterIndex = options.Path.IndexOf('/');
var containerName = delimiterIndex > 0 ? options.Path[..delimiterIndex] : options.Path;
var client = new BlobContainerClient(connectionString, containerName);
await foreach (var blob in client.GetBlobsAsync()) var prefix = delimiterIndex > 0 ? options.Path[(delimiterIndex + 1)..] : string.Empty;
await foreach (var blob in client.GetBlobsByHierarchyAsync(default, default, delimiter: "/", prefix))
{ {
System.Console.Write(options.ContainerName); System.Console.Write(containerName);
System.Console.Write('/'); System.Console.Write('/');
System.Console.WriteLine(blob.Name);
if (blob.IsPrefix)
{
System.Console.WriteLine(blob.Prefix);
}
else if (blob.IsBlob)
{
System.Console.WriteLine(blob.Blob.Name);
}
else
{
throw new NotImplementedException("Unknown result - neither a blob nor a prefix (folder).");
}
} }
} }

View file

@ -0,0 +1,17 @@
namespace System.Collections.Immutable
{
public static class ImmutableArrayExtensions
{
public static ImmutableArray<T> ToImmutableArray<T>(this ReadOnlySpan<T> span)
{
var array = ImmutableArray.CreateBuilder<T>(span.Length);
foreach (var value in span)
{
array.Add(value);
}
return array.MoveToImmutable();
}
}
}

View file

@ -10,7 +10,13 @@ namespace Shamir.Console
public static async Task<int> Main(string[] args) public static async Task<int> Main(string[] args)
{ {
await using var serviceProvider = new ServiceCollection() await using var serviceProvider = new ServiceCollection()
.AddTransient<Parser>(sp => new Parser(with => with.EnableDashDash = true)) .AddTransient<Parser>(sp => new Parser(with =>
{
with.AutoHelp = true;
with.AutoVersion = true;
with.EnableDashDash = true;
with.IgnoreUnknownArguments = false;
}))
.BuildServiceProvider(); .BuildServiceProvider();
var tree = new DefaultCommandTree( var tree = new DefaultCommandTree(