diff --git a/Tapeti.Annotations/Tapeti.Annotations.csproj b/Tapeti.Annotations/Tapeti.Annotations.csproj index 11c27dc..7dc38fc 100644 --- a/Tapeti.Annotations/Tapeti.Annotations.csproj +++ b/Tapeti.Annotations/Tapeti.Annotations.csproj @@ -24,4 +24,7 @@ + + + diff --git a/Tapeti.Autofac/Tapeti.Autofac.csproj b/Tapeti.Autofac/Tapeti.Autofac.csproj index dfc6146..b441b47 100644 --- a/Tapeti.Autofac/Tapeti.Autofac.csproj +++ b/Tapeti.Autofac/Tapeti.Autofac.csproj @@ -28,4 +28,7 @@ + + + diff --git a/Tapeti.CastleWindsor/Tapeti.CastleWindsor.csproj b/Tapeti.CastleWindsor/Tapeti.CastleWindsor.csproj index b2623cf..d8546d8 100644 --- a/Tapeti.CastleWindsor/Tapeti.CastleWindsor.csproj +++ b/Tapeti.CastleWindsor/Tapeti.CastleWindsor.csproj @@ -28,4 +28,7 @@ + + + diff --git a/Tapeti.Cmd/ConsoleHelper/ConsoleWrapper.cs b/Tapeti.Cmd/ConsoleHelper/ConsoleWrapper.cs index 4f5aaee..6c2bcc8 100644 --- a/Tapeti.Cmd/ConsoleHelper/ConsoleWrapper.cs +++ b/Tapeti.Cmd/ConsoleHelper/ConsoleWrapper.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Text; using System.Threading; +using Console = System.Console; namespace Tapeti.Cmd.ConsoleHelper { @@ -138,39 +140,79 @@ namespace Tapeti.Cmd.ConsoleHelper public abstract bool Enabled { get; } + public abstract void WriteCaptured(string value, Action processInput); public abstract void WriteLine(string value); public void Confirm(string message) { WriteLine(message); - TryReadKey(false, out _); + + // Clear any previous key entered before this confirmation + while (!Owner.Cancelled && Console.KeyAvailable) + Console.ReadKey(true); + + while (!Owner.Cancelled && !Console.KeyAvailable) + Thread.Sleep(50); + + if (Owner.Cancelled) + return; + + Console.ReadKey(true); } public bool ConfirmYesNo(string message) { - WriteLine($"{message} (Y/N) "); - if (!TryReadKey(true, out var key)) - return false; + var confirmed = false; - return key.KeyChar == 'y' || key.KeyChar == 'Y'; - } - - - private bool TryReadKey(bool showKeyOutput, out ConsoleKeyInfo keyInfo) - { - while (!Owner.Cancelled && !Console.KeyAvailable) - Thread.Sleep(50); - - if (Owner.Cancelled) + WriteCaptured($"{message} (Y/N) ", () => { - keyInfo = default; - return false; - } + // Clear any previous key entered before this confirmation + while (!Owner.Cancelled && Console.KeyAvailable) + Console.ReadKey(true); - keyInfo = Console.ReadKey(!showKeyOutput); - return true; + var input = new StringBuilder(); + + while (!Owner.Cancelled) + { + if (!Console.KeyAvailable) + { + Thread.Sleep(50); + continue; + } + + var keyInfo = Console.ReadKey(false); + + // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault - by design + switch (keyInfo.Key) + { + case ConsoleKey.Enter: + Console.WriteLine(); + confirmed = input.ToString().Equals("Y", StringComparison.CurrentCultureIgnoreCase); + return; + + case ConsoleKey.Backspace: + if (input.Length > 0) + { + input.Remove(input.Length - 1, 1); + + // We need to handle erasing the character ourselves, as we want to use ReadKey so that we can monitor Cancelled + Console.Write(" \b"); + } + + break; + + default: + if (keyInfo.KeyChar != -1) + input.Append(keyInfo.KeyChar); + + break; + } + } + }); + + return confirmed; } } @@ -185,6 +227,22 @@ namespace Tapeti.Cmd.ConsoleHelper public override bool Enabled => true; + + public override void WriteCaptured(string value, Action waitForInput) + { + Owner.AcquirePermanent(); + try + { + Console.Write(value); + waitForInput(); + } + finally + { + Owner.ReleasePermanent(); + } + } + + public override void WriteLine(string value) { Owner.AcquirePermanent(); @@ -217,6 +275,13 @@ namespace Tapeti.Cmd.ConsoleHelper public override bool Enabled => !Console.IsOutputRedirected; + public override void WriteCaptured(string value, Action waitForInput) + { + WriteLine(value); + waitForInput(); + } + + public override void WriteLine(string value) { if (!Enabled) diff --git a/Tapeti.Cmd/RateLimiter/BatchSizeRateLimiter.cs b/Tapeti.Cmd/RateLimiter/BatchSizeRateLimiter.cs index c9e8ed9..058682a 100644 --- a/Tapeti.Cmd/RateLimiter/BatchSizeRateLimiter.cs +++ b/Tapeti.Cmd/RateLimiter/BatchSizeRateLimiter.cs @@ -26,7 +26,7 @@ namespace Tapeti.Cmd.RateLimiter if (batchCount > batchSize) { Pause(console); - batchCount = 0; + batchCount = 1; } decoratedRateLimiter.Execute(action); diff --git a/Tapeti.Cmd/Serialization/EasyNetQMessageSerializer.cs b/Tapeti.Cmd/Serialization/EasyNetQMessageSerializer.cs index 570fb02..cabbcb3 100644 --- a/Tapeti.Cmd/Serialization/EasyNetQMessageSerializer.cs +++ b/Tapeti.Cmd/Serialization/EasyNetQMessageSerializer.cs @@ -31,6 +31,12 @@ namespace Tapeti.Cmd.Serialization } + public static bool OutputExists(string path) + { + return Directory.Exists(path) && Directory.GetFiles(path, "*.message.txt").Length > 0; + } + + public void Dispose() { } diff --git a/Tapeti.Cmd/Verbs/ExportVerb.cs b/Tapeti.Cmd/Verbs/ExportVerb.cs index d6313ef..db7b023 100644 --- a/Tapeti.Cmd/Verbs/ExportVerb.cs +++ b/Tapeti.Cmd/Verbs/ExportVerb.cs @@ -2,7 +2,6 @@ using System.IO; using System.Text; using CommandLine; -using RabbitMQ.Client; using Tapeti.Cmd.ConsoleHelper; using Tapeti.Cmd.Serialization; @@ -17,10 +16,16 @@ namespace Tapeti.Cmd.Verbs [Option('o', "output", Required = true, HelpText = "Path or filename (depending on the chosen serialization method) where the messages will be output to.")] public string OutputPath { get; set; } + + [Option('y', "overwrite", HelpText = "If the output exists, do not ask to overwrite.")] + public bool Overwrite { get; set; } [Option('r', "remove", HelpText = "If specified messages are acknowledged and removed from the queue. If not messages are kept.")] public bool RemoveMessages { get; set; } + [Option("skip", HelpText = "(Default: 0) Number of messages in the queue to skip. Useful if a previous non-removing export was interrupted.", Default = 0)] + public int Skip { get; set; } + [Option('n', "maxcount", HelpText = "(Default: all) Maximum number of messages to retrieve from the queue.")] public int? MaxCount { get; set; } } @@ -40,12 +45,21 @@ namespace Tapeti.Cmd.Verbs public void Execute(IConsole console) { var consoleWriter = console.GetPermanentWriter(); + + using var messageSerializer = GetMessageSerializer(options, consoleWriter); + if (messageSerializer == null) + return; + var factory = options.CreateConnectionFactory(console); - using var messageSerializer = GetMessageSerializer(options); using var connection = factory.CreateConnection(); using var channel = connection.CreateModel(); var totalCount = (int)channel.MessageCount(options.QueueName); + + var skip = Math.Max(options.Skip, 0); + if (skip > 0) + totalCount -= Math.Min(skip, totalCount); + if (options.MaxCount.HasValue && options.MaxCount.Value < totalCount) totalCount = options.MaxCount.Value; @@ -61,24 +75,28 @@ namespace Tapeti.Cmd.Verbs // No more messages on the queue break; - messageCount++; - - messageSerializer.Serialize(new Message + if (skip > 0) + skip--; + else { - DeliveryTag = result.DeliveryTag, - Redelivered = result.Redelivered, - Exchange = result.Exchange, - RoutingKey = result.RoutingKey, - Queue = options.QueueName, - Properties = result.BasicProperties, - Body = result.Body.ToArray() - }); + messageCount++; - if (options.RemoveMessages) - channel.BasicAck(result.DeliveryTag, false); + messageSerializer.Serialize(new Message + { + DeliveryTag = result.DeliveryTag, + Redelivered = result.Redelivered, + Exchange = result.Exchange, + RoutingKey = result.RoutingKey, + Queue = options.QueueName, + Properties = result.BasicProperties, + Body = result.Body.ToArray() + }); + if (options.RemoveMessages) + channel.BasicAck(result.DeliveryTag, false); - progressBar.Report(messageCount); + progressBar.Report(messageCount); + } } } @@ -86,16 +104,27 @@ namespace Tapeti.Cmd.Verbs } - private static IMessageSerializer GetMessageSerializer(ExportOptions options) + private static IMessageSerializer GetMessageSerializer(ExportOptions options, IConsoleWriter consoleWriter) { switch (options.SerializationMethod) { case SerializationMethod.SingleFileJSON: + // ReSharper disable once InvertIf - causes two lines of "new SingleFileJSONMessageSerializer". DRY ReSharper. + if (!options.Overwrite && File.Exists(options.OutputPath)) + { + if (!consoleWriter.ConfirmYesNo($"The output file '{options.OutputPath}' already exists, do you want to overwrite it?")) + return null; + } + return new SingleFileJSONMessageSerializer(new FileStream(options.OutputPath, FileMode.Create, FileAccess.Write, FileShare.Read), true, Encoding.UTF8); case SerializationMethod.EasyNetQHosepipe: - if (string.IsNullOrEmpty(options.OutputPath)) - throw new ArgumentException("An output path must be provided when using EasyNetQHosepipe serialization"); + // ReSharper disable once InvertIf - causes two lines of "new SingleFileJSONMessageSerializer". DRY ReSharper. + if (!options.Overwrite && EasyNetQMessageSerializer.OutputExists(options.OutputPath)) + { + if (!consoleWriter.ConfirmYesNo($"The output path '{options.OutputPath}' already contains a previous export, do you want to overwrite it?")) + return null; + } return new EasyNetQMessageSerializer(options.OutputPath); diff --git a/Tapeti.Cmd/Verbs/ImportVerb.cs b/Tapeti.Cmd/Verbs/ImportVerb.cs index 87fa434..441d812 100644 --- a/Tapeti.Cmd/Verbs/ImportVerb.cs +++ b/Tapeti.Cmd/Verbs/ImportVerb.cs @@ -28,6 +28,12 @@ namespace Tapeti.Cmd.Verbs [Option('e', "exchange", HelpText = "If specified publishes to the originating exchange using the original routing key. By default these are ignored and the message is published directly to the originating queue.")] public bool PublishToExchange { get; set; } + + [Option("skip", HelpText = "(Default: 0) Number of messages in the input to skip. Useful if a previous import was interrupted.", Default = 0)] + public int Skip { get; set; } + + [Option('n', "maxcount", HelpText = "(Default: all) Maximum number of messages to import.")] + public int? MaxCount { get; set; } [Option("maxrate", HelpText = "The maximum amount of messages per second to import.")] public int? MaxRate { get; set; } @@ -61,6 +67,7 @@ namespace Tapeti.Cmd.Verbs var totalCount = messageSerializer.GetMessageCount(); var messageCount = 0; + var skip = Math.Max(options.Skip, 0); ProgressBar progress = null; @@ -70,24 +77,27 @@ namespace Tapeti.Cmd.Verbs { foreach (var message in messageSerializer.Deserialize(channel)) { - if (console.Cancelled) + if (console.Cancelled || (options.MaxCount.HasValue && messageCount >= options.MaxCount.Value)) break; - rateLimiter.Execute(() => - { - if (console.Cancelled) - return; + if (skip > 0) + skip--; + else + rateLimiter.Execute(() => + { + if (console.Cancelled) + return; - var exchange = options.PublishToExchange ? message.Exchange : ""; - var routingKey = options.PublishToExchange ? message.RoutingKey : message.Queue; + var exchange = options.PublishToExchange ? message.Exchange : ""; + var routingKey = options.PublishToExchange ? message.RoutingKey : message.Queue; - // ReSharper disable AccessToDisposedClosure - channel.BasicPublish(exchange, routingKey, message.Properties, message.Body); - messageCount++; - - progress?.Report(messageCount); - // ReSharper restore AccessToDisposedClosure - }); + // ReSharper disable AccessToDisposedClosure + channel.BasicPublish(exchange, routingKey, message.Properties, message.Body); + messageCount++; + + progress?.Report(messageCount); + // ReSharper restore AccessToDisposedClosure + }); } } finally diff --git a/Tapeti.Cmd/Verbs/ShovelVerb.cs b/Tapeti.Cmd/Verbs/ShovelVerb.cs index 891abe8..18e4c4e 100644 --- a/Tapeti.Cmd/Verbs/ShovelVerb.cs +++ b/Tapeti.Cmd/Verbs/ShovelVerb.cs @@ -37,6 +37,9 @@ namespace Tapeti.Cmd.Verbs [Option("targetpassword", HelpText = "Password used to connect to the target RabbitMQ server. Defaults to the source password.")] public string TargetPassword { get; set; } + [Option("skip", HelpText = "(Default: 0) Number of messages in the queue to skip. Useful if a previous non-removing shovel was interrupted.", Default = 0)] + public int Skip { get; set; } + [Option("maxrate", HelpText = "The maximum amount of messages per second to shovel.")] public int? MaxRate { get; set; } @@ -93,6 +96,11 @@ namespace Tapeti.Cmd.Verbs var targetQueueName = !string.IsNullOrEmpty(options.TargetQueueName) ? options.TargetQueueName : options.QueueName; var totalCount = (int)sourceChannel.MessageCount(options.QueueName); + + var skip = Math.Max(options.Skip, 0); + if (skip > 0) + totalCount -= Math.Min(skip, totalCount); + if (options.MaxCount.HasValue && options.MaxCount.Value < totalCount) totalCount = options.MaxCount.Value; @@ -101,6 +109,18 @@ namespace Tapeti.Cmd.Verbs using (var progressBar = new ProgressBar(console, totalCount)) { + // Perform the skips outside of the rate limiter + while (skip > 0 && !console.Cancelled) + { + var result = sourceChannel.BasicGet(options.QueueName, false); + if (result == null) + // No more messages on the queue + return; + + skip--; + } + + var hasMessage = true; while (!console.Cancelled && hasMessage && (!options.MaxCount.HasValue || messageCount < options.MaxCount.Value)) diff --git a/Tapeti.DataAnnotations.Extensions/Tapeti.DataAnnotations.Extensions.csproj b/Tapeti.DataAnnotations.Extensions/Tapeti.DataAnnotations.Extensions.csproj index 3de7536..0e62b2b 100644 --- a/Tapeti.DataAnnotations.Extensions/Tapeti.DataAnnotations.Extensions.csproj +++ b/Tapeti.DataAnnotations.Extensions/Tapeti.DataAnnotations.Extensions.csproj @@ -28,4 +28,7 @@ + + + diff --git a/Tapeti.DataAnnotations/Tapeti.DataAnnotations.csproj b/Tapeti.DataAnnotations/Tapeti.DataAnnotations.csproj index 27bb111..17f465c 100644 --- a/Tapeti.DataAnnotations/Tapeti.DataAnnotations.csproj +++ b/Tapeti.DataAnnotations/Tapeti.DataAnnotations.csproj @@ -32,4 +32,7 @@ + + + diff --git a/Tapeti.Flow.SQL/Tapeti.Flow.SQL.csproj b/Tapeti.Flow.SQL/Tapeti.Flow.SQL.csproj index c23f096..f3aaee5 100644 --- a/Tapeti.Flow.SQL/Tapeti.Flow.SQL.csproj +++ b/Tapeti.Flow.SQL/Tapeti.Flow.SQL.csproj @@ -41,4 +41,7 @@ + + + diff --git a/Tapeti.Flow/Tapeti.Flow.csproj b/Tapeti.Flow/Tapeti.Flow.csproj index 40f2f79..b1d4c95 100644 --- a/Tapeti.Flow/Tapeti.Flow.csproj +++ b/Tapeti.Flow/Tapeti.Flow.csproj @@ -29,4 +29,7 @@ + + + diff --git a/Tapeti.Ninject/Tapeti.Ninject.csproj b/Tapeti.Ninject/Tapeti.Ninject.csproj index c16b08e..994a749 100644 --- a/Tapeti.Ninject/Tapeti.Ninject.csproj +++ b/Tapeti.Ninject/Tapeti.Ninject.csproj @@ -28,4 +28,7 @@ + + + diff --git a/Tapeti.Serilog/Tapeti.Serilog.csproj b/Tapeti.Serilog/Tapeti.Serilog.csproj index 25d7d29..69efdee 100644 --- a/Tapeti.Serilog/Tapeti.Serilog.csproj +++ b/Tapeti.Serilog/Tapeti.Serilog.csproj @@ -32,4 +32,7 @@ + + + diff --git a/Tapeti.SimpleInjector/Tapeti.SimpleInjector.csproj b/Tapeti.SimpleInjector/Tapeti.SimpleInjector.csproj index 050dd5d..de35d46 100644 --- a/Tapeti.SimpleInjector/Tapeti.SimpleInjector.csproj +++ b/Tapeti.SimpleInjector/Tapeti.SimpleInjector.csproj @@ -33,4 +33,7 @@ + + + diff --git a/Tapeti.Transient/Tapeti.Transient.csproj b/Tapeti.Transient/Tapeti.Transient.csproj index 02df581..90f345a 100644 --- a/Tapeti.Transient/Tapeti.Transient.csproj +++ b/Tapeti.Transient/Tapeti.Transient.csproj @@ -28,4 +28,7 @@ + + + diff --git a/Tapeti.UnityContainer/Tapeti.UnityContainer.csproj b/Tapeti.UnityContainer/Tapeti.UnityContainer.csproj index 42399ca..bbd37c2 100644 --- a/Tapeti.UnityContainer/Tapeti.UnityContainer.csproj +++ b/Tapeti.UnityContainer/Tapeti.UnityContainer.csproj @@ -28,4 +28,7 @@ + + + diff --git a/Tapeti/Config/IMessageContext.cs b/Tapeti/Config/IMessageContext.cs index ac6a0e5..7d2db6d 100644 --- a/Tapeti/Config/IMessageContext.cs +++ b/Tapeti/Config/IMessageContext.cs @@ -8,7 +8,7 @@ namespace Tapeti.Config /// /// Provides information about the message currently being handled. /// - public interface IMessageContext : IAsyncDisposable, IDisposable + public interface IMessageContext : IAsyncDisposable { /// /// Provides access to the Tapeti config. diff --git a/Tapeti/Default/MessageContext.cs b/Tapeti/Default/MessageContext.cs index 6a001af..40a153c 100644 --- a/Tapeti/Default/MessageContext.cs +++ b/Tapeti/Default/MessageContext.cs @@ -66,21 +66,21 @@ namespace Tapeti.Default } - /// - public void Dispose() - { - foreach (var payload in payloads.Values) - (payload as IDisposable)?.Dispose(); - } - - /// public async ValueTask DisposeAsync() { foreach (var payload in payloads.Values) { - if (payload is IAsyncDisposable asyncDisposable) - await asyncDisposable.DisposeAsync(); + switch (payload) + { + case IAsyncDisposable asyncDisposable: + await asyncDisposable.DisposeAsync(); + break; + + case IDisposable disposable: + disposable.Dispose(); + break; + } } } diff --git a/Tapeti/Tapeti.csproj b/Tapeti/Tapeti.csproj index 4751189..cb55780 100644 --- a/Tapeti/Tapeti.csproj +++ b/Tapeti/Tapeti.csproj @@ -36,4 +36,7 @@ + + + diff --git a/appveyor.yml b/appveyor.yml index 5680f86..d666e25 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,19 +11,19 @@ before_build: after_build: # Create NuGet packages - - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output output Tapeti\Tapeti.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% - - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output output Tapeti.Annotations\Tapeti.Annotations.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% - - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output output Tapeti.DataAnnotations\Tapeti.DataAnnotations.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% - - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output output Tapeti.DataAnnotations.Extensions\Tapeti.DataAnnotations.Extensions.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% - - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output output Tapeti.Flow\Tapeti.Flow.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% - - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output output Tapeti.Flow.SQL\Tapeti.Flow.SQL.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% - - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output output Tapeti.Transient\Tapeti.Transient.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% - - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output output Tapeti.Serilog\Tapeti.Serilog.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% - - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output output Tapeti.SimpleInjector\Tapeti.SimpleInjector.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% - - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output output Tapeti.Autofac\Tapeti.Autofac.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% - - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output output Tapeti.CastleWindsor\Tapeti.CastleWindsor.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% - - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output output Tapeti.Ninject\Tapeti.Ninject.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% - - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output output Tapeti.UnityContainer\Tapeti.UnityContainer.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% + - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:PublishRepositoryUrl=true --output output Tapeti\Tapeti.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% + - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:PublishRepositoryUrl=true --output output Tapeti.Annotations\Tapeti.Annotations.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% + - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:PublishRepositoryUrl=true --output output Tapeti.DataAnnotations\Tapeti.DataAnnotations.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% + - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:PublishRepositoryUrl=true --output output Tapeti.DataAnnotations.Extensions\Tapeti.DataAnnotations.Extensions.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% + - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:PublishRepositoryUrl=true --output output Tapeti.Flow\Tapeti.Flow.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% + - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:PublishRepositoryUrl=true --output output Tapeti.Flow.SQL\Tapeti.Flow.SQL.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% + - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:PublishRepositoryUrl=true --output output Tapeti.Transient\Tapeti.Transient.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% + - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:PublishRepositoryUrl=true --output output Tapeti.Serilog\Tapeti.Serilog.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% + - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:PublishRepositoryUrl=true --output output Tapeti.SimpleInjector\Tapeti.SimpleInjector.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% + - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:PublishRepositoryUrl=true --output output Tapeti.Autofac\Tapeti.Autofac.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% + - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:PublishRepositoryUrl=true --output output Tapeti.CastleWindsor\Tapeti.CastleWindsor.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% + - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:PublishRepositoryUrl=true --output output Tapeti.Ninject\Tapeti.Ninject.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% + - cmd: dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:PublishRepositoryUrl=true --output output Tapeti.UnityContainer\Tapeti.UnityContainer.csproj /p:Configuration=Release /p:Version=%GitVersion_NuGetVersion% # Create Tapeti.Cmd release - cmd: dotnet publish -c Release -r win-x64 --self-contained=true -o publish\x64\selfcontained Tapeti.Cmd\Tapeti.Cmd.csproj - cmd: dotnet publish -c Release -r win-x64 --self-contained=false -o publish\x64 Tapeti.Cmd\Tapeti.Cmd.csproj diff --git a/docs/tapeticmd.rst b/docs/tapeticmd.rst index a474baf..b271c4b 100644 --- a/docs/tapeticmd.rst +++ b/docs/tapeticmd.rst @@ -45,9 +45,15 @@ Fetches messages from a queue and writes them to disk. -o , --output *Required*. Path or filename (depending on the chosen serialization method) where the messages will be output to. +-y, --overwrite + If the output exists, do not ask to overwrite. + -r, --remove If specified messages are acknowledged and removed from the queue. If not messages are kept. +--skip + Number of messages in the input to skip. Useful if a previous non-removing export was interrupted. + -n , --maxcount Maximum number of messages to retrieve from the queue. If not specified all messages are exported. @@ -79,6 +85,12 @@ Read messages from disk as previously exported and publish them to a queue. -e, --exchange If specified publishes to the originating exchange using the original routing key. By default these are ignored and the message is published directly to the originating queue. +--skip + Number of messages in the input to skip. Useful if a previous import was interrupted. + +-n , --maxcount + Maximum number of messages to import. If not specified all messages are imported. + -s , --serialization The method used to serialize the message for import or export. Valid options: SingleFileJSON, EasyNetQHosepipe. Defaults to SingleFileJSON. See Serialization methods below for more information. @@ -115,6 +127,9 @@ Reads messages from a queue and publishes them to another queue, optionally to a -r, --remove If specified messages are acknowledged and removed from the queue. If not messages are kept. +--skip + Number of messages in the input to skip. Useful if a previous non-removing shovel was interrupted. + -n , --maxcount Maximum number of messages to retrieve from the queue. If not specified all messages are exported.