diff --git a/src/SeqCli/Cli/Commands/App/UninstallCommand.cs b/src/SeqCli/Cli/Commands/App/UninstallCommand.cs new file mode 100644 index 00000000..c9d275a9 --- /dev/null +++ b/src/SeqCli/Cli/Commands/App/UninstallCommand.cs @@ -0,0 +1,64 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using SeqCli.Cli.Features; +using SeqCli.Connection; +using SeqCli.Util; +using Serilog; + +namespace SeqCli.Cli.Commands.App; + +[Command("app", "uninstall", "Uninstall an app package", + Example = "seqcli app uninstall --package-id 'Seq.App.JsonArchive'")] +// ReSharper disable once UnusedType.Global +class UninstallCommand : Command +{ + readonly SeqConnectionFactory _connectionFactory; + + string? _packageId, _id; + readonly ConnectionFeature _connection; + + public UninstallCommand(SeqConnectionFactory connectionFactory) + { + _connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory)); + + Options.Add( + "package-id=", + "The package id of the app package to uninstall", + packageId => _packageId = ArgumentString.Normalize(packageId)); + + Options.Add( + "i=|id=", + "The id of a single app package to uninstall", + t => _id = ArgumentString.Normalize(t)); + + _connection = Enable(); + } + + protected override async Task Run() + { + if (_packageId == null && _id == null) + { + Log.Error("A `package-id` or `id` must be specified"); + return 1; + } + + var connection = _connectionFactory.Connect(_connection); + + var toRemove = _id != null ? [await connection.Apps.FindAsync(_id)] + : (await connection.Apps.ListAsync()) + .Where(app => _packageId == app.Package.PackageId) + .ToArray(); + + if (!toRemove.Any()) + { + Log.Error("No matching API key was found"); + return 1; + } + + foreach (var app in toRemove) + await connection.Apps.RemoveAsync(app); + + return 0; + } +} diff --git a/src/SeqCli/Cli/Features/EntityIdentityFeature.cs b/src/SeqCli/Cli/Features/EntityIdentityFeature.cs index e15dbcc6..a6f9dab2 100644 --- a/src/SeqCli/Cli/Features/EntityIdentityFeature.cs +++ b/src/SeqCli/Cli/Features/EntityIdentityFeature.cs @@ -14,6 +14,7 @@ using System; using System.Collections.Generic; +using SeqCli.Util; namespace SeqCli.Cli.Features; @@ -22,8 +23,6 @@ class EntityIdentityFeature : CommandFeature readonly string _entityName; readonly string _verb; - string? _title, _id; - public EntityIdentityFeature(string entityName, string verb) { _entityName = entityName ?? throw new ArgumentNullException(nameof(entityName)); @@ -35,12 +34,12 @@ public override void Enable(OptionSet options) options.Add( "t=|title=", $"The title of the {_entityName}(s) to {_verb}", - t => _title = t); + t => Title = ArgumentString.Normalize(t)); options.Add( "i=|id=", $"The id of a single {_entityName} to {_verb}", - t => _id = t); + t => Id = ArgumentString.Normalize(t)); } public override IEnumerable GetUsageErrors() @@ -49,7 +48,7 @@ public override IEnumerable GetUsageErrors() yield return "Only one of either `title` or `id` can be specified"; } - public string? Title => string.IsNullOrWhiteSpace(_title) ? null : _title.Trim(); + public string? Title { get; private set; } - public string? Id => string.IsNullOrWhiteSpace(_id) ? null : _id.Trim(); + public string? Id { get; private set; } } \ No newline at end of file diff --git a/test/SeqCli.EndToEnd/App/AppBasicsTestCase.cs b/test/SeqCli.EndToEnd/App/AppBasicsTestCase.cs index 638ca8b0..d8c8d63f 100644 --- a/test/SeqCli.EndToEnd/App/AppBasicsTestCase.cs +++ b/test/SeqCli.EndToEnd/App/AppBasicsTestCase.cs @@ -23,6 +23,9 @@ public Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRun exit = runner.Exec("app update", "--all"); Assert.Equal(0, exit); + exit = runner.Exec("app uninstall", "--package-id Seq.App.EmailPlus"); + Assert.Equal(0, exit); + return Task.CompletedTask; } }