From d71927915c795fbc7c15d70a096f788d9272e19f Mon Sep 17 00:00:00 2001 From: Menno van Lavieren Date: Mon, 14 Aug 2017 13:58:01 +0200 Subject: [PATCH 01/20] SqlFlow Repository interface aangepast --- Tapeti.Flow.SQL/ConfigExtensions.cs | 2 +- Tapeti.Flow.SQL/SqlConnectionFlowRepository.cs | 8 ++++---- Tapeti.Flow/ConfigExtensions.cs | 2 +- Tapeti.Flow/Default/FlowStore.cs | 6 +++--- Tapeti.Flow/Default/NonPersistentFlowRepository.cs | 8 ++++---- Tapeti.Flow/FlowMiddleware.cs | 6 +++--- Tapeti.Flow/IFlowRepository.cs | 8 ++++---- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Tapeti.Flow.SQL/ConfigExtensions.cs b/Tapeti.Flow.SQL/ConfigExtensions.cs index 70b1aff..c5e660d 100644 --- a/Tapeti.Flow.SQL/ConfigExtensions.cs +++ b/Tapeti.Flow.SQL/ConfigExtensions.cs @@ -30,7 +30,7 @@ namespace Tapeti.Flow.SQL public void RegisterDefaults(IDependencyContainer container) { - container.RegisterDefault>(() => new SqlConnectionFlowRepository(connectionString, serviceId, schema)); + container.RegisterDefault(() => new SqlConnectionFlowRepository(connectionString, serviceId, schema)); } diff --git a/Tapeti.Flow.SQL/SqlConnectionFlowRepository.cs b/Tapeti.Flow.SQL/SqlConnectionFlowRepository.cs index 4af9d05..78e47a7 100644 --- a/Tapeti.Flow.SQL/SqlConnectionFlowRepository.cs +++ b/Tapeti.Flow.SQL/SqlConnectionFlowRepository.cs @@ -24,7 +24,7 @@ namespace Tapeti.Flow.SQL ); go; */ - public class SqlConnectionFlowRepository : IFlowRepository + public class SqlConnectionFlowRepository : IFlowRepository { private readonly string connectionString; private readonly int serviceId; @@ -39,7 +39,7 @@ namespace Tapeti.Flow.SQL } - public async Task>> GetStates() + public async Task>> GetStates() { using (var connection = await GetConnection()) { @@ -69,14 +69,14 @@ namespace Tapeti.Flow.SQL } - public Task CreateState(Guid flowID, T state, DateTime timestamp) + public Task CreateState(Guid flowID, T state, DateTime timestamp) { var stateJason = JsonConvert.SerializeObject(state); throw new NotImplementedException(); } - public Task UpdateState(Guid flowID, T state) + public Task UpdateState(Guid flowID, T state) { throw new NotImplementedException(); } diff --git a/Tapeti.Flow/ConfigExtensions.cs b/Tapeti.Flow/ConfigExtensions.cs index 843333e..127a0c2 100644 --- a/Tapeti.Flow/ConfigExtensions.cs +++ b/Tapeti.Flow/ConfigExtensions.cs @@ -2,7 +2,7 @@ { public static class ConfigExtensions { - public static TapetiConfig WithFlow(this TapetiConfig config, IFlowRepository flowRepository = null) + public static TapetiConfig WithFlow(this TapetiConfig config, IFlowRepository flowRepository = null) { config.Use(new FlowMiddleware(flowRepository)); return config; diff --git a/Tapeti.Flow/Default/FlowStore.cs b/Tapeti.Flow/Default/FlowStore.cs index 8bb1061..b4e63ba 100644 --- a/Tapeti.Flow/Default/FlowStore.cs +++ b/Tapeti.Flow/Default/FlowStore.cs @@ -13,10 +13,10 @@ namespace Tapeti.Flow.Default private static readonly ConcurrentDictionary FlowStates = new ConcurrentDictionary(); private static readonly ConcurrentDictionary ContinuationLookup = new ConcurrentDictionary(); - private readonly IFlowRepository repository; + private readonly IFlowRepository repository; - public FlowStore(IFlowRepository repository) + public FlowStore(IFlowRepository repository) { this.repository = repository; } @@ -27,7 +27,7 @@ namespace Tapeti.Flow.Default FlowStates.Clear(); ContinuationLookup.Clear(); - foreach (var flowStateRecord in await repository.GetStates()) + foreach (var flowStateRecord in await repository.GetStates()) { FlowStates.TryAdd(flowStateRecord.Key, flowStateRecord.Value); diff --git a/Tapeti.Flow/Default/NonPersistentFlowRepository.cs b/Tapeti.Flow/Default/NonPersistentFlowRepository.cs index dee4e78..00ec009 100644 --- a/Tapeti.Flow/Default/NonPersistentFlowRepository.cs +++ b/Tapeti.Flow/Default/NonPersistentFlowRepository.cs @@ -5,19 +5,19 @@ using System.Threading.Tasks; namespace Tapeti.Flow.Default { - public class NonPersistentFlowRepository : IFlowRepository + public class NonPersistentFlowRepository : IFlowRepository { - Task>> IFlowRepository.GetStates() + Task>> IFlowRepository.GetStates() { return Task.FromResult(new List>()); } - public Task CreateState(Guid flowID, T state, DateTime timestamp) + public Task CreateState(Guid flowID, T state, DateTime timestamp) { return Task.CompletedTask; } - public Task UpdateState(Guid flowID, T state) + public Task UpdateState(Guid flowID, T state) { return Task.CompletedTask; } diff --git a/Tapeti.Flow/FlowMiddleware.cs b/Tapeti.Flow/FlowMiddleware.cs index cba399d..bef12a5 100644 --- a/Tapeti.Flow/FlowMiddleware.cs +++ b/Tapeti.Flow/FlowMiddleware.cs @@ -6,9 +6,9 @@ namespace Tapeti.Flow { public class FlowMiddleware : ITapetiExtension { - private IFlowRepository flowRepository; + private IFlowRepository flowRepository; - public FlowMiddleware(IFlowRepository flowRepository) + public FlowMiddleware(IFlowRepository flowRepository) { this.flowRepository = flowRepository; } @@ -18,7 +18,7 @@ namespace Tapeti.Flow container.RegisterDefault(); container.RegisterDefault(); container.RegisterDefault(); - container.RegisterDefault>(() => flowRepository ?? new NonPersistentFlowRepository()); + container.RegisterDefault(() => flowRepository ?? new NonPersistentFlowRepository()); container.RegisterDefault(); } diff --git a/Tapeti.Flow/IFlowRepository.cs b/Tapeti.Flow/IFlowRepository.cs index ce026d9..0f5dbb4 100644 --- a/Tapeti.Flow/IFlowRepository.cs +++ b/Tapeti.Flow/IFlowRepository.cs @@ -5,11 +5,11 @@ using System.Threading.Tasks; namespace Tapeti.Flow { - public interface IFlowRepository + public interface IFlowRepository { - Task>> GetStates(); - Task CreateState(Guid flowID, T state, DateTime timestamp); - Task UpdateState(Guid flowID, T state); + Task>> GetStates(); + Task CreateState(Guid flowID, T state, DateTime timestamp); + Task UpdateState(Guid flowID, T state); Task DeleteState(Guid flowID); } From 9f30c1ec74870550b2bc6f20a245021e6514fdb0 Mon Sep 17 00:00:00 2001 From: Menno van Lavieren Date: Wed, 30 Aug 2017 17:47:43 +0200 Subject: [PATCH 02/20] Tapeti Consumer HandleBasicDeliver schedules a Task asynchronously to handle the messages --- Tapeti/Connection/TapetiConsumer.cs | 91 ++++++++++++++++------------- Tapeti/Default/ConsoleLogger.cs | 9 ++- Tapeti/Default/DevNullLogger.cs | 8 ++- Tapeti/ILogger.cs | 5 +- Test/Program.cs | 1 + 5 files changed, 69 insertions(+), 45 deletions(-) diff --git a/Tapeti/Connection/TapetiConsumer.cs b/Tapeti/Connection/TapetiConsumer.cs index d47a498..a6e5209 100644 --- a/Tapeti/Connection/TapetiConsumer.cs +++ b/Tapeti/Connection/TapetiConsumer.cs @@ -17,6 +17,8 @@ namespace Tapeti.Connection private readonly IDependencyResolver dependencyResolver; private readonly IReadOnlyList messageMiddleware; private readonly List bindings; + + private readonly ILogger logger; private readonly IExceptionStrategy exceptionStrategy; @@ -28,6 +30,7 @@ namespace Tapeti.Connection this.messageMiddleware = messageMiddleware; this.bindings = bindings.ToList(); + logger = dependencyResolver.Resolve(); exceptionStrategy = dependencyResolver.Resolve(); } @@ -35,59 +38,65 @@ namespace Tapeti.Connection public override void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, IBasicProperties properties, byte[] body) { - ExceptionDispatchInfo exception = null; - try + Task.Run(async () => { - var message = dependencyResolver.Resolve().Deserialize(body, properties); - if (message == null) - throw new ArgumentException("Empty message"); - - var validMessageType = false; - - using (var context = new MessageContext + ExceptionDispatchInfo exception = null; + try { - DependencyResolver = dependencyResolver, - Queue = queueName, - RoutingKey = routingKey, - Message = message, - Properties = properties - }) - { - try + var message = dependencyResolver.Resolve().Deserialize(body, properties); + if (message == null) + throw new ArgumentException("Empty message"); + + var validMessageType = false; + + using (var context = new MessageContext { - foreach (var binding in bindings) + DependencyResolver = dependencyResolver, + Queue = queueName, + RoutingKey = routingKey, + Message = message, + Properties = properties + }) + { + try { - if (binding.Accept(context, message)) + foreach (var binding in bindings) { - InvokeUsingBinding(context, binding, message); + if (binding.Accept(context, message)) + { + await InvokeUsingBinding(context, binding, message); - validMessageType = true; + validMessageType = true; + } } + + if (!validMessageType) + throw new ArgumentException($"Unsupported message type: {message.GetType().FullName}"); + + await worker.Respond(deliveryTag, ConsumeResponse.Ack); + } + catch (Exception e) + { + exception = ExceptionDispatchInfo.Capture(UnwrapException(e)); + await worker.Respond(deliveryTag, exceptionStrategy.HandleException(context, exception.SourceException)); } - - if (!validMessageType) - throw new ArgumentException($"Unsupported message type: {message.GetType().FullName}"); - - worker.Respond(deliveryTag, ConsumeResponse.Ack); - } - catch (Exception e) - { - exception = ExceptionDispatchInfo.Capture(UnwrapException(e)); - worker.Respond(deliveryTag, exceptionStrategy.HandleException(context, exception.SourceException)); } } - } - catch (Exception e) - { - exception = ExceptionDispatchInfo.Capture(UnwrapException(e)); - worker.Respond(deliveryTag, exceptionStrategy.HandleException(null, exception.SourceException)); - } + catch (Exception e) + { + exception = ExceptionDispatchInfo.Capture(UnwrapException(e)); + await worker.Respond(deliveryTag, exceptionStrategy.HandleException(null, exception.SourceException)); + } - exception?.Throw(); + if (exception != null) + { + logger.HandlerException(exception.SourceException); + } + }); } - private void InvokeUsingBinding(MessageContext context, IBinding binding, object message) + private Task InvokeUsingBinding(MessageContext context, IBinding binding, object message) { context.Binding = binding; @@ -136,9 +145,7 @@ namespace Tapeti.Connection await binding.Invoke(c, message); }); - firstCaller.Call(context) - .Wait(); - + return firstCaller.Call(context); } private static Exception UnwrapException(Exception exception) diff --git a/Tapeti/Default/ConsoleLogger.cs b/Tapeti/Default/ConsoleLogger.cs index 2cb7caf..98484fe 100644 --- a/Tapeti/Default/ConsoleLogger.cs +++ b/Tapeti/Default/ConsoleLogger.cs @@ -1,4 +1,6 @@ -namespace Tapeti.Default +using System; + +namespace Tapeti.Default { public class ConsoleLogger : ILogger { @@ -16,5 +18,10 @@ { throw new System.NotImplementedException(); } + + public void HandlerException(Exception e) + { + Console.WriteLine(e.ToString()); + } } } diff --git a/Tapeti/Default/DevNullLogger.cs b/Tapeti/Default/DevNullLogger.cs index 24919fc..af4ce57 100644 --- a/Tapeti/Default/DevNullLogger.cs +++ b/Tapeti/Default/DevNullLogger.cs @@ -1,4 +1,6 @@ -namespace Tapeti.Default +using System; + +namespace Tapeti.Default { public class DevNullLogger : ILogger { @@ -13,5 +15,9 @@ public void ConnectSuccess(TapetiConnectionParams connectionParams) { } + + public void HandlerException(Exception e) + { + } } } diff --git a/Tapeti/ILogger.cs b/Tapeti/ILogger.cs index 014f217..1dc244d 100644 --- a/Tapeti/ILogger.cs +++ b/Tapeti/ILogger.cs @@ -1,4 +1,6 @@ -namespace Tapeti +using System; + +namespace Tapeti { // This interface is deliberately specific and typed to allow for structured logging (e.g. Serilog) // instead of only string-based logging without control over the output. @@ -7,5 +9,6 @@ void Connect(TapetiConnectionParams connectionParams); void ConnectFailed(TapetiConnectionParams connectionParams); void ConnectSuccess(TapetiConnectionParams connectionParams); + void HandlerException(Exception e); } } diff --git a/Test/Program.cs b/Test/Program.cs index 6eecc55..0e97ee9 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -20,6 +20,7 @@ namespace Test var container = new Container(); container.Register(); container.Register(); + container.Register(); //container.Register(() => new EF(serviceID)); From 84f1511b57f56968ad086fcfbbf6714d164ebe5f Mon Sep 17 00:00:00 2001 From: Menno van Lavieren Date: Thu, 7 Sep 2017 09:55:26 +0200 Subject: [PATCH 03/20] RDB-46 Volgorde bug in Flow Library Uitbreiding van het testscript --- Test/FlowEndController.cs | 42 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/Test/FlowEndController.cs b/Test/FlowEndController.cs index 522b98f..679e542 100644 --- a/Test/FlowEndController.cs +++ b/Test/FlowEndController.cs @@ -1,6 +1,7 @@ using System; using Tapeti.Annotations; using Tapeti.Flow; +using Tapeti.Flow.Annotations; namespace Test { @@ -17,7 +18,25 @@ namespace Test public IYieldPoint StartFlow(PingMessage message) { - Console.WriteLine("PingMessage received, call flowProvider.End()"); + Console.WriteLine("PingMessage received, calling flowProvider.End() directly"); + + if (DateTime.Now < new DateTime(2000, 1, 1)) + { + //never true + return flowProvider + .YieldWithRequestSync + (new PingConfirmationRequestMessage() { StoredInState = "Ping:" }, + HandlePingConfirmationResponse); + } + + return Finish(); + } + + + [Continuation] + public IYieldPoint HandlePingConfirmationResponse(PingConfirmationResponseMessage msg) + { + Console.WriteLine("Ending ping flow: " + msg.Answer); return Finish(); } @@ -33,5 +52,26 @@ namespace Test } + [Request(Response = typeof(PingConfirmationResponseMessage))] + public class PingConfirmationRequestMessage + { + public string StoredInState { get; set; } + } + + + public class PingConfirmationResponseMessage + { + public string Answer { get; set; } + } + + public PingConfirmationResponseMessage PingConfirmation(PingConfirmationRequestMessage message) + { + Console.WriteLine(">> receive Ping (returning pong)"); + + return new PingConfirmationResponseMessage + { + Answer = message.StoredInState + " Pong!" + }; + } } } From 7245424c157bb6570a41f2db8d1301156f992424 Mon Sep 17 00:00:00 2001 From: Lou Baudoin Date: Fri, 22 Sep 2017 11:19:49 +0200 Subject: [PATCH 04/20] RDB-93 Regressietest - RDB-125 Extern leadopvolging komt niet in Nico's Mock --- Tapeti.Flow/Default/FlowStarter.cs | 30 ++++++++++++++++++++++++++---- Tapeti.Flow/IFlowProvider.cs | 2 ++ Test/MarcoController.cs | 12 ++++++++---- Test/Program.cs | 3 ++- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/Tapeti.Flow/Default/FlowStarter.cs b/Tapeti.Flow/Default/FlowStarter.cs index a21593f..215decb 100644 --- a/Tapeti.Flow/Default/FlowStarter.cs +++ b/Tapeti.Flow/Default/FlowStarter.cs @@ -20,20 +20,30 @@ namespace Tapeti.Flow.Default public Task Start(Expression>> methodSelector) where TController : class { - return CallControllerMethod(GetExpressionMethod(methodSelector), value => Task.FromResult((IYieldPoint)value)); + return CallControllerMethod(GetExpressionMethod(methodSelector), value => Task.FromResult((IYieldPoint)value), new object[] { }); } public Task Start(Expression>>> methodSelector) where TController : class { - return CallControllerMethod(GetExpressionMethod(methodSelector), value => (Task)value); + return CallControllerMethod(GetExpressionMethod(methodSelector), value => (Task)value, new object[] {}); + } + + public Task Start(Expression>> methodSelector, TParameter parameter) where TController : class + { + return CallControllerMethod(GetExpressionMethod(methodSelector), value => Task.FromResult((IYieldPoint)value), new object[] {parameter}); + } + + public Task Start(Expression>>> methodSelector, TParameter parameter) where TController : class + { + return CallControllerMethod(GetExpressionMethod(methodSelector), value => (Task)value, new object[] {parameter}); } - private async Task CallControllerMethod(MethodInfo method, Func> getYieldPointResult) where TController : class + private async Task CallControllerMethod(MethodInfo method, Func> getYieldPointResult, object[] parameters) where TController : class { var controller = config.DependencyResolver.Resolve(); - var yieldPoint = await getYieldPointResult(method.Invoke(controller, new object[] {})); + var yieldPoint = await getYieldPointResult(method.Invoke(controller, parameters)); var context = new MessageContext { @@ -57,5 +67,17 @@ namespace Tapeti.Flow.Default return method; } + + private static MethodInfo GetExpressionMethod(Expression>> methodSelector) + { + var callExpression = (methodSelector.Body as UnaryExpression)?.Operand as MethodCallExpression; + var targetMethodExpression = callExpression?.Object as ConstantExpression; + + var method = targetMethodExpression?.Value as MethodInfo; + if (method == null) + throw new ArgumentException("Unable to determine the starting method", nameof(methodSelector)); + + return method; + } } } diff --git a/Tapeti.Flow/IFlowProvider.cs b/Tapeti.Flow/IFlowProvider.cs index f4bc7d1..edb3968 100644 --- a/Tapeti.Flow/IFlowProvider.cs +++ b/Tapeti.Flow/IFlowProvider.cs @@ -27,6 +27,8 @@ namespace Tapeti.Flow { Task Start(Expression>> methodSelector) where TController : class; Task Start(Expression>>> methodSelector) where TController : class; + Task Start(Expression>> methodSelector, TParameter parameter) where TController : class; + Task Start(Expression>>> methodSelector, TParameter parameter) where TController : class; } /// diff --git a/Test/MarcoController.cs b/Test/MarcoController.cs index 1bf8ae3..a3a65f4 100644 --- a/Test/MarcoController.cs +++ b/Test/MarcoController.cs @@ -29,14 +29,18 @@ namespace Test [Start] - public async Task StartFlow() + public async Task StartFlow(bool go) { Console.WriteLine("Starting stand-alone flow"); await Task.Delay(1000); - return flowProvider.YieldWithRequestSync - (new PoloConfirmationRequestMessage(), - HandlePoloConfirmationResponse); + if (go) + return flowProvider.YieldWithRequestSync + (new PoloConfirmationRequestMessage(), + HandlePoloConfirmationResponse); + + Console.WriteLine("Ending stand-alone flow prematurely"); + return flowProvider.End(); } diff --git a/Test/Program.cs b/Test/Program.cs index 0e97ee9..dc9fef8 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using SimpleInjector; using Tapeti; using Tapeti.DataAnnotations; @@ -55,7 +56,7 @@ namespace Test connection.GetPublisher().Publish(new FlowEndController.PingMessage()); - container.GetInstance().Start(c => c.StartFlow); + container.GetInstance().Start(c => c.StartFlow, true); var emitter = container.GetInstance(); emitter.Run().Wait(); From 3c440742895de0d0184611b1eaf92850ce7fbef2 Mon Sep 17 00:00:00 2001 From: Menno van Lavieren Date: Fri, 13 Oct 2017 10:49:59 +0200 Subject: [PATCH 05/20] RDB-136 Flow tabel wordt niet meer opgeruimd Bij een exceptie in Flow Consummer HandleBasicDeliver aangepast zodat het makkelijker is om een dispatch ronde toe tevoegen voor cleanup handlers --- Tapeti/Connection/TapetiConsumer.cs | 109 ++++++++++++++++++---------- 1 file changed, 71 insertions(+), 38 deletions(-) diff --git a/Tapeti/Connection/TapetiConsumer.cs b/Tapeti/Connection/TapetiConsumer.cs index a6e5209..4bfb1a7 100644 --- a/Tapeti/Connection/TapetiConsumer.cs +++ b/Tapeti/Connection/TapetiConsumer.cs @@ -41,60 +41,93 @@ namespace Tapeti.Connection Task.Run(async () => { ExceptionDispatchInfo exception = null; + MessageContext context = null; + ConsumeResponse response = ConsumeResponse.Nack; try { - var message = dependencyResolver.Resolve().Deserialize(body, properties); - if (message == null) - throw new ArgumentException("Empty message"); - - var validMessageType = false; - - using (var context = new MessageContext + try { - DependencyResolver = dependencyResolver, - Queue = queueName, - RoutingKey = routingKey, - Message = message, - Properties = properties - }) + context = new MessageContext + { + DependencyResolver = dependencyResolver, + Queue = queueName, + RoutingKey = routingKey, + Properties = properties + }; + + await DispatchMesage(context, body); + + response = ConsumeResponse.Ack; + } + catch (Exception eDispatch) { + exception = ExceptionDispatchInfo.Capture(UnwrapException(eDispatch)); + logger.HandlerException(eDispatch); try { - foreach (var binding in bindings) - { - if (binding.Accept(context, message)) - { - await InvokeUsingBinding(context, binding, message); - - validMessageType = true; - } - } - - if (!validMessageType) - throw new ArgumentException($"Unsupported message type: {message.GetType().FullName}"); - - await worker.Respond(deliveryTag, ConsumeResponse.Ack); + response = exceptionStrategy.HandleException(null, exception.SourceException); } - catch (Exception e) + catch (Exception eStrategy) { - exception = ExceptionDispatchInfo.Capture(UnwrapException(e)); - await worker.Respond(deliveryTag, exceptionStrategy.HandleException(context, exception.SourceException)); + logger.HandlerException(eStrategy); } } + try + { + // Cleanup handlers + //response = exceptionStrategy.HandleException(null, exception.SourceException); + } + catch (Exception eCleanup) + { + logger.HandlerException(eCleanup); + } } - catch (Exception e) + finally { - exception = ExceptionDispatchInfo.Capture(UnwrapException(e)); - await worker.Respond(deliveryTag, exceptionStrategy.HandleException(null, exception.SourceException)); - } - - if (exception != null) - { - logger.HandlerException(exception.SourceException); + try + { + await worker.Respond(deliveryTag, response); + } + catch (Exception eRespond) + { + logger.HandlerException(eRespond); + } + try + { + if (context != null) + { + context.Dispose(); + } + } + catch (Exception eDispose) + { + logger.HandlerException(eDispose); + } } }); } + private async Task DispatchMesage(MessageContext context, byte[] body) + { + var message = dependencyResolver.Resolve().Deserialize(body, context.Properties); + if (message == null) + throw new ArgumentException("Empty message"); + + var validMessageType = false; + + foreach (var binding in bindings) + { + if (binding.Accept(context, message)) + { + await InvokeUsingBinding(context, binding, message); + + validMessageType = true; + } + } + + if (!validMessageType) + throw new ArgumentException($"Unsupported message type: {message.GetType().FullName}"); + } private Task InvokeUsingBinding(MessageContext context, IBinding binding, object message) { From 625c80e8f736b47f54ff92c36c2b3c7701dd9bac Mon Sep 17 00:00:00 2001 From: Menno van Lavieren Date: Fri, 13 Oct 2017 13:07:41 +0200 Subject: [PATCH 06/20] RDB-136 Flow tabel wordt niet meer opgeruimd Bij een exceptie in Flow Mogelijkheid voor het registreren van Cleanup middleware in Tapeti --- Tapeti/Config/ICleanupMiddleware.cs | 13 +++++++++++++ Tapeti/Config/IConfig.cs | 1 + Tapeti/Connection/TapetiConsumer.cs | 22 +++++++++++++++++++--- Tapeti/Connection/TapetiWorker.cs | 2 +- Tapeti/Tapeti.csproj | 1 + Tapeti/TapetiConfig.cs | 16 ++++++++++++++-- 6 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 Tapeti/Config/ICleanupMiddleware.cs diff --git a/Tapeti/Config/ICleanupMiddleware.cs b/Tapeti/Config/ICleanupMiddleware.cs new file mode 100644 index 0000000..e056ad0 --- /dev/null +++ b/Tapeti/Config/ICleanupMiddleware.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tapeti.Config +{ + public interface ICleanupMiddleware + { + Task Handle(IMessageContext context, ConsumeResponse response); + } +} diff --git a/Tapeti/Config/IConfig.cs b/Tapeti/Config/IConfig.cs index a7cd268..46bf575 100644 --- a/Tapeti/Config/IConfig.cs +++ b/Tapeti/Config/IConfig.cs @@ -9,6 +9,7 @@ namespace Tapeti.Config { IDependencyResolver DependencyResolver { get; } IReadOnlyList MessageMiddleware { get; } + IReadOnlyList CleanupMiddleware { get; } IReadOnlyList PublishMiddleware { get; } IEnumerable Queues { get; } diff --git a/Tapeti/Connection/TapetiConsumer.cs b/Tapeti/Connection/TapetiConsumer.cs index 4bfb1a7..9c7a118 100644 --- a/Tapeti/Connection/TapetiConsumer.cs +++ b/Tapeti/Connection/TapetiConsumer.cs @@ -16,18 +16,20 @@ namespace Tapeti.Connection private readonly string queueName; private readonly IDependencyResolver dependencyResolver; private readonly IReadOnlyList messageMiddleware; + private readonly IReadOnlyList cleanupMiddleware; private readonly List bindings; private readonly ILogger logger; private readonly IExceptionStrategy exceptionStrategy; - public TapetiConsumer(TapetiWorker worker, string queueName, IDependencyResolver dependencyResolver, IEnumerable bindings, IReadOnlyList messageMiddleware) + public TapetiConsumer(TapetiWorker worker, string queueName, IDependencyResolver dependencyResolver, IEnumerable bindings, IReadOnlyList messageMiddleware, IReadOnlyList cleanupMiddleware) { this.worker = worker; this.queueName = queueName; this.dependencyResolver = dependencyResolver; this.messageMiddleware = messageMiddleware; + this.cleanupMiddleware = cleanupMiddleware; this.bindings = bindings.ToList(); logger = dependencyResolver.Resolve(); @@ -74,8 +76,7 @@ namespace Tapeti.Connection } try { - // Cleanup handlers - //response = exceptionStrategy.HandleException(null, exception.SourceException); + await RunCleanup(context, response); } catch (Exception eCleanup) { @@ -107,6 +108,21 @@ namespace Tapeti.Connection }); } + private async Task RunCleanup(MessageContext context, ConsumeResponse response) + { + foreach(var handler in cleanupMiddleware) + { + try + { + await handler.Handle(context, response); + } + catch (Exception eCleanup) + { + logger.HandlerException(eCleanup); + } + } + } + private async Task DispatchMesage(MessageContext context, byte[] body) { var message = dependencyResolver.Resolve().Deserialize(body, context.Properties); diff --git a/Tapeti/Connection/TapetiWorker.cs b/Tapeti/Connection/TapetiWorker.cs index 8b9f7d6..e539bf7 100644 --- a/Tapeti/Connection/TapetiWorker.cs +++ b/Tapeti/Connection/TapetiWorker.cs @@ -57,7 +57,7 @@ namespace Tapeti.Connection return taskQueue.Value.Add(async () => { - (await GetChannel()).BasicConsume(queueName, false, new TapetiConsumer(this, queueName, config.DependencyResolver, bindings, config.MessageMiddleware)); + (await GetChannel()).BasicConsume(queueName, false, new TapetiConsumer(this, queueName, config.DependencyResolver, bindings, config.MessageMiddleware, config.CleanupMiddleware)); }).Unwrap(); } diff --git a/Tapeti/Tapeti.csproj b/Tapeti/Tapeti.csproj index 493548a..f41e903 100644 --- a/Tapeti/Tapeti.csproj +++ b/Tapeti/Tapeti.csproj @@ -53,6 +53,7 @@ + diff --git a/Tapeti/TapetiConfig.cs b/Tapeti/TapetiConfig.cs index 75ed930..b8ed17b 100644 --- a/Tapeti/TapetiConfig.cs +++ b/Tapeti/TapetiConfig.cs @@ -26,6 +26,7 @@ namespace Tapeti private readonly List bindingMiddleware = new List(); private readonly List messageMiddleware = new List(); + private readonly List cleanupMiddleware = new List(); private readonly List publishMiddleware = new List(); private readonly IDependencyResolver dependencyResolver; @@ -62,7 +63,7 @@ namespace Tapeti queues.AddRange(dynamicBindings.Select(bl => new Queue(new QueueInfo { Dynamic = true }, bl))); - var config = new Config(dependencyResolver, messageMiddleware, publishMiddleware, queues); + var config = new Config(dependencyResolver, messageMiddleware, cleanupMiddleware, publishMiddleware, queues); (dependencyResolver as IDependencyContainer)?.RegisterDefaultSingleton(config); return config; @@ -83,6 +84,13 @@ namespace Tapeti } + public TapetiConfig Use(ICleanupMiddleware handler) + { + cleanupMiddleware.Add(handler); + return this; + } + + public TapetiConfig Use(IPublishMiddleware handler) { publishMiddleware.Add(handler); @@ -108,6 +116,8 @@ namespace Tapeti Use((IBindingMiddleware)middleware); else if (middleware is IMessageMiddleware) Use((IMessageMiddleware)middleware); + else if (middleware is ICleanupMiddleware) + Use((ICleanupMiddleware)middleware); else if (middleware is IPublishMiddleware) Use((IPublishMiddleware)middleware); else @@ -345,16 +355,18 @@ namespace Tapeti { public IDependencyResolver DependencyResolver { get; } public IReadOnlyList MessageMiddleware { get; } + public IReadOnlyList CleanupMiddleware { get; } public IReadOnlyList PublishMiddleware { get; } public IEnumerable Queues { get; } private readonly Dictionary bindingMethodLookup; - public Config(IDependencyResolver dependencyResolver, IReadOnlyList messageMiddleware, IReadOnlyList publishMiddleware, IEnumerable queues) + public Config(IDependencyResolver dependencyResolver, IReadOnlyList messageMiddleware, IReadOnlyList cleanupMiddleware, IReadOnlyList publishMiddleware, IEnumerable queues) { DependencyResolver = dependencyResolver; MessageMiddleware = messageMiddleware; + CleanupMiddleware = cleanupMiddleware; PublishMiddleware = publishMiddleware; Queues = queues.ToList(); From 355c992dbf9cc0b1a0932ed09cd0fa5ee32e3b33 Mon Sep 17 00:00:00 2001 From: Menno van Lavieren Date: Fri, 13 Oct 2017 13:49:47 +0200 Subject: [PATCH 07/20] RDB-136 Flow tabel wordt niet meer opgeruimd Bij een exceptie in Flow Flow ruimt nu zijn locks goed op en de weggeschreven state ingeval van exceptions tijdens het afhandelen van de message --- Tapeti.Flow/Default/FlowCleanupMiddleware.cs | 29 ++++++++++++++++++++ Tapeti.Flow/FlowMiddleware.cs | 3 +- Tapeti.Flow/Tapeti.Flow.csproj | 1 + Tapeti/Connection/TapetiConsumer.cs | 2 ++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 Tapeti.Flow/Default/FlowCleanupMiddleware.cs diff --git a/Tapeti.Flow/Default/FlowCleanupMiddleware.cs b/Tapeti.Flow/Default/FlowCleanupMiddleware.cs new file mode 100644 index 0000000..e0420c8 --- /dev/null +++ b/Tapeti.Flow/Default/FlowCleanupMiddleware.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tapeti.Config; + +namespace Tapeti.Flow.Default +{ + public class FlowCleanupMiddleware : ICleanupMiddleware + { + public async Task Handle(IMessageContext context, ConsumeResponse response) + { + object flowContextObj; + if (!context.Items.TryGetValue(ContextItems.FlowContext, out flowContextObj)) + return; + var flowContext = (FlowContext)flowContextObj; + + if (flowContext.FlowStateLock != null) + { + if (response == ConsumeResponse.Nack) + { + await flowContext.FlowStateLock.DeleteFlowState(); + } + flowContext.FlowStateLock.Dispose(); + } + } + } +} diff --git a/Tapeti.Flow/FlowMiddleware.cs b/Tapeti.Flow/FlowMiddleware.cs index bef12a5..fb13d90 100644 --- a/Tapeti.Flow/FlowMiddleware.cs +++ b/Tapeti.Flow/FlowMiddleware.cs @@ -24,7 +24,8 @@ namespace Tapeti.Flow public IEnumerable GetMiddleware(IDependencyResolver dependencyResolver) { - return new[] { new FlowBindingMiddleware() }; + yield return new FlowBindingMiddleware(); + yield return new FlowCleanupMiddleware(); } } } diff --git a/Tapeti.Flow/Tapeti.Flow.csproj b/Tapeti.Flow/Tapeti.Flow.csproj index b085913..5a4b9fd 100644 --- a/Tapeti.Flow/Tapeti.Flow.csproj +++ b/Tapeti.Flow/Tapeti.Flow.csproj @@ -54,6 +54,7 @@ + diff --git a/Tapeti/Connection/TapetiConsumer.cs b/Tapeti/Connection/TapetiConsumer.cs index 9c7a118..3beb9ed 100644 --- a/Tapeti/Connection/TapetiConsumer.cs +++ b/Tapeti/Connection/TapetiConsumer.cs @@ -129,6 +129,8 @@ namespace Tapeti.Connection if (message == null) throw new ArgumentException("Empty message"); + context.Message = message; + var validMessageType = false; foreach (var binding in bindings) From 3ce847c21fbd27145e32edff724a93e8a5314309 Mon Sep 17 00:00:00 2001 From: Menno van Lavieren Date: Tue, 17 Oct 2017 10:34:07 +0200 Subject: [PATCH 08/20] RDB-46 Volgorde bug in Flow Library Refactoring van de FlowStore en de FlowPovider met de oplossing voor de bug --- Tapeti.Flow/Default/DelegateYieldPoint.cs | 7 +- Tapeti.Flow/Default/FlowBindingMiddleware.cs | 2 +- Tapeti.Flow/Default/FlowCleanupMiddleware.cs | 2 +- Tapeti.Flow/Default/FlowContext.cs | 22 ++- .../Default/FlowMessageFilterMiddleware.cs | 2 - Tapeti.Flow/Default/FlowProvider.cs | 94 ++++++----- Tapeti.Flow/Default/FlowStarter.cs | 31 +++- Tapeti.Flow/Default/FlowState.cs | 17 +- Tapeti.Flow/Default/FlowStore.cs | 148 +++++++----------- Tapeti.Flow/Default/IExecutableYieldPoint.cs | 10 -- Tapeti.Flow/Default/StateYieldPoint.cs | 22 --- Tapeti.Flow/FlowHelpers/LockCollection.cs | 110 +++++++++++++ Tapeti.Flow/FlowMiddleware.cs | 4 +- Tapeti.Flow/Tapeti.Flow.csproj | 3 +- Test/MarcoController.cs | 25 ++- Test/MarcoEmitter.cs | 2 +- Test/Program.cs | 8 + 17 files changed, 307 insertions(+), 202 deletions(-) delete mode 100644 Tapeti.Flow/Default/IExecutableYieldPoint.cs delete mode 100644 Tapeti.Flow/Default/StateYieldPoint.cs create mode 100644 Tapeti.Flow/FlowHelpers/LockCollection.cs diff --git a/Tapeti.Flow/Default/DelegateYieldPoint.cs b/Tapeti.Flow/Default/DelegateYieldPoint.cs index 984f4cd..2ed0926 100644 --- a/Tapeti.Flow/Default/DelegateYieldPoint.cs +++ b/Tapeti.Flow/Default/DelegateYieldPoint.cs @@ -3,16 +3,13 @@ using System.Threading.Tasks; namespace Tapeti.Flow.Default { - internal class DelegateYieldPoint : IExecutableYieldPoint + internal class DelegateYieldPoint : IYieldPoint { - public bool StoreState { get; } - private readonly Func onExecute; - public DelegateYieldPoint(bool storeState, Func onExecute) + public DelegateYieldPoint(Func onExecute) { - StoreState = storeState; this.onExecute = onExecute; } diff --git a/Tapeti.Flow/Default/FlowBindingMiddleware.cs b/Tapeti.Flow/Default/FlowBindingMiddleware.cs index d7e279a..9053135 100644 --- a/Tapeti.Flow/Default/FlowBindingMiddleware.cs +++ b/Tapeti.Flow/Default/FlowBindingMiddleware.cs @@ -87,7 +87,7 @@ namespace Tapeti.Flow.Default private static Task HandleParallelResponse(IMessageContext context) { var flowHandler = context.DependencyResolver.Resolve(); - return flowHandler.Execute(context, new StateYieldPoint(true)); + return flowHandler.Execute(context, new DelegateYieldPoint((a) => Task.CompletedTask)); } diff --git a/Tapeti.Flow/Default/FlowCleanupMiddleware.cs b/Tapeti.Flow/Default/FlowCleanupMiddleware.cs index e0420c8..811be59 100644 --- a/Tapeti.Flow/Default/FlowCleanupMiddleware.cs +++ b/Tapeti.Flow/Default/FlowCleanupMiddleware.cs @@ -16,7 +16,7 @@ namespace Tapeti.Flow.Default return; var flowContext = (FlowContext)flowContextObj; - if (flowContext.FlowStateLock != null) + if (flowContext?.FlowStateLock != null) { if (response == ConsumeResponse.Nack) { diff --git a/Tapeti.Flow/Default/FlowContext.cs b/Tapeti.Flow/Default/FlowContext.cs index 460d5b2..dbadf08 100644 --- a/Tapeti.Flow/Default/FlowContext.cs +++ b/Tapeti.Flow/Default/FlowContext.cs @@ -13,13 +13,13 @@ namespace Tapeti.Flow.Default public Guid ContinuationID { get; set; } public ContinuationMetadata ContinuationMetadata { get; set; } - private bool stored; + private bool storeCalled; + private bool deleteCalled; - public async Task EnsureStored() + public async Task Store() { - if (stored) - return; + storeCalled = true; if (MessageContext == null) throw new ArgumentNullException(nameof(MessageContext)); if (FlowState == null) throw new ArgumentNullException(nameof(FlowState)); @@ -27,8 +27,20 @@ namespace Tapeti.Flow.Default FlowState.Data = Newtonsoft.Json.JsonConvert.SerializeObject(MessageContext.Controller); await FlowStateLock.StoreFlowState(FlowState); + } - stored = true; + public async Task Delete() + { + deleteCalled = true; + + if (FlowStateLock != null) + await FlowStateLock.DeleteFlowState(); + } + + public void EnsureStoreOrDeleteIsCalled() + { + if (!storeCalled && !deleteCalled) + throw new InvalidProgramException("Neither Store nor Delete are called for the state of the current flow. FlowID = " + FlowStateLock?.FlowID); } public void Dispose() diff --git a/Tapeti.Flow/Default/FlowMessageFilterMiddleware.cs b/Tapeti.Flow/Default/FlowMessageFilterMiddleware.cs index 1d327ae..8c66e9d 100644 --- a/Tapeti.Flow/Default/FlowMessageFilterMiddleware.cs +++ b/Tapeti.Flow/Default/FlowMessageFilterMiddleware.cs @@ -39,8 +39,6 @@ namespace Tapeti.Flow.Default return null; var flowStateLock = await flowStore.LockFlowState(flowID.Value); - if (flowStateLock == null) - return null; var flowState = await flowStateLock.GetFlowState(); if (flowState == null) diff --git a/Tapeti.Flow/Default/FlowProvider.cs b/Tapeti.Flow/Default/FlowProvider.cs index 88bc9f6..308cb3a 100644 --- a/Tapeti.Flow/Default/FlowProvider.cs +++ b/Tapeti.Flow/Default/FlowProvider.cs @@ -26,13 +26,13 @@ namespace Tapeti.Flow.Default public IYieldPoint YieldWithRequest(TRequest message, Func> responseHandler) { var responseHandlerInfo = GetResponseHandlerInfo(config, message, responseHandler); - return new DelegateYieldPoint(true, context => SendRequest(context, message, responseHandlerInfo)); + return new DelegateYieldPoint(context => SendRequest(context, message, responseHandlerInfo)); } public IYieldPoint YieldWithRequestSync(TRequest message, Func responseHandler) { var responseHandlerInfo = GetResponseHandlerInfo(config, message, responseHandler); - return new DelegateYieldPoint(true, context => SendRequest(context, message, responseHandlerInfo)); + return new DelegateYieldPoint(context => SendRequest(context, message, responseHandlerInfo)); } public IFlowParallelRequestBuilder YieldWithParallelRequest() @@ -42,18 +42,23 @@ namespace Tapeti.Flow.Default public IYieldPoint EndWithResponse(TResponse message) { - return new DelegateYieldPoint(false, context => SendResponse(context, message)); + return new DelegateYieldPoint(context => SendResponse(context, message)); } public IYieldPoint End() { - return new DelegateYieldPoint(false, EndFlow); + return new DelegateYieldPoint(EndFlow); } private async Task SendRequest(FlowContext context, object message, ResponseHandlerInfo responseHandlerInfo, string convergeMethodName = null, bool convergeMethodTaskSync = false) { + if (context.FlowState == null) + { + await CreateNewFlowState(context); + } + var continuationID = Guid.NewGuid(); context.FlowState.Continuations.Add(continuationID, @@ -70,14 +75,18 @@ namespace Tapeti.Flow.Default ReplyTo = responseHandlerInfo.ReplyToQueue }; - await context.EnsureStored(); + await context.Store(); + await publisher.Publish(message, properties); } private async Task SendResponse(FlowContext context, object message) { - var reply = context.FlowState.Metadata.Reply; + var reply = context.FlowState == null + ? GetReply(context.MessageContext) + : context.FlowState.Metadata.Reply; + if (reply == null) throw new YieldPointException("No response is required"); @@ -92,19 +101,21 @@ namespace Tapeti.Flow.Default properties.CorrelationId = reply.CorrelationId; // TODO disallow if replyto is not specified? - if (context.FlowState.Metadata.Reply.ReplyTo != null) + if (reply.ReplyTo != null) await publisher.PublishDirect(message, reply.ReplyTo, properties); else await publisher.Publish(message, properties); + + await context.Delete(); } - private static Task EndFlow(FlowContext context) + private static async Task EndFlow(FlowContext context) { - if (context.FlowState.Metadata.Reply != null) - throw new YieldPointException($"Flow must end with a response message of type {context.FlowState.Metadata.Reply.ResponseTypeName}"); + await context.Delete(); - return Task.CompletedTask; + if (context.FlowState != null && context.FlowState.Metadata.Reply != null) + throw new YieldPointException($"Flow must end with a response message of type {context.FlowState.Metadata.Reply.ResponseTypeName}"); } @@ -147,11 +158,31 @@ namespace Tapeti.Flow.Default }; } + private async Task CreateNewFlowState(FlowContext flowContext) + { + var flowStore = flowContext.MessageContext.DependencyResolver.Resolve(); + + var flowID = Guid.NewGuid(); + flowContext.FlowStateLock = await flowStore.LockFlowState(flowID); + + if (flowContext.FlowStateLock == null) + throw new InvalidOperationException("Unable to lock a new flow"); + + flowContext.FlowState = new FlowState + { + Metadata = new FlowMetadata + { + Reply = GetReply(flowContext.MessageContext) + } + }; + } public async Task Execute(IMessageContext context, IYieldPoint yieldPoint) { - var executableYieldPoint = yieldPoint as IExecutableYieldPoint; - var storeState = executableYieldPoint?.StoreState ?? false; + var executableYieldPoint = yieldPoint as DelegateYieldPoint; + + if (executableYieldPoint == null) + throw new YieldPointException($"Yield point is required in controller {context.Controller.GetType().Name} for method {context.Binding.Method.Name}"); FlowContext flowContext; object flowContextItem; @@ -160,27 +191,10 @@ namespace Tapeti.Flow.Default { flowContext = new FlowContext { - MessageContext = context, - FlowState = new FlowState() + MessageContext = context }; - if (storeState) - { - // Initiate the flow - var flowStore = context.DependencyResolver.Resolve(); - - var flowID = Guid.NewGuid(); - flowContext.FlowStateLock = await flowStore.LockFlowState(flowID); - - if (flowContext.FlowStateLock == null) - throw new InvalidOperationException("Unable to lock a new flow"); - - flowContext.FlowState = await flowContext.FlowStateLock.GetFlowState(); - if (flowContext.FlowState == null) - throw new InvalidOperationException("Unable to get state for new flow"); - - flowContext.FlowState.Metadata.Reply = GetReply(context); - } + context.Items.Add(ContextItems.FlowContext, flowContext); } else flowContext = (FlowContext)flowContextItem; @@ -193,19 +207,17 @@ namespace Tapeti.Flow.Default } catch (YieldPointException e) { - var controllerName = flowContext.MessageContext.Controller.GetType().FullName; - var methodName = flowContext.MessageContext.Binding.Method.Name; - - throw new YieldPointException($"{e.Message} in controller {controllerName}, method {methodName}", e); + // Useful for debugging + e.Data["Tapeti.Controller.Name"] = context.Controller.GetType().FullName; + e.Data["Tapeti.Controller.Method"] = context.Binding.Method.Name; + throw; } - if (storeState) - await flowContext.EnsureStored(); - else if (flowContext.FlowStateLock != null) - await flowContext.FlowStateLock.DeleteFlowState(); + flowContext.EnsureStoreOrDeleteIsCalled(); } + private class ParallelRequestBuilder : IFlowParallelRequestBuilder { public delegate Task SendRequestFunc(FlowContext context, @@ -275,7 +287,7 @@ namespace Tapeti.Flow.Default if (convergeMethod?.Method == null) throw new ArgumentNullException(nameof(convergeMethod)); - return new DelegateYieldPoint(true, context => + return new DelegateYieldPoint(context => { if (convergeMethod.Method.DeclaringType != context.MessageContext.Controller.GetType()) throw new YieldPointException("Converge method must be in the same controller class"); diff --git a/Tapeti.Flow/Default/FlowStarter.cs b/Tapeti.Flow/Default/FlowStarter.cs index 215decb..d376f65 100644 --- a/Tapeti.Flow/Default/FlowStarter.cs +++ b/Tapeti.Flow/Default/FlowStarter.cs @@ -10,11 +10,13 @@ namespace Tapeti.Flow.Default public class FlowStarter : IFlowStarter { private readonly IConfig config; + private readonly ILogger logger; - public FlowStarter(IConfig config) + public FlowStarter(IConfig config, ILogger logger) { this.config = config; + this.logger = logger; } @@ -52,7 +54,32 @@ namespace Tapeti.Flow.Default }; var flowHandler = config.DependencyResolver.Resolve(); - await flowHandler.Execute(context, yieldPoint); + + ConsumeResponse response = ConsumeResponse.Nack; + try + { + await flowHandler.Execute(context, yieldPoint); + response = ConsumeResponse.Ack; + } + finally + { + await RunCleanup(context, response); + } + } + + private async Task RunCleanup(MessageContext context, ConsumeResponse response) + { + foreach (var handler in config.CleanupMiddleware) + { + try + { + await handler.Handle(context, response); + } + catch (Exception eCleanup) + { + logger.HandlerException(eCleanup); + } + } } diff --git a/Tapeti.Flow/Default/FlowState.cs b/Tapeti.Flow/Default/FlowState.cs index e1cb0cf..0ee3eec 100644 --- a/Tapeti.Flow/Default/FlowState.cs +++ b/Tapeti.Flow/Default/FlowState.cs @@ -25,20 +25,13 @@ namespace Tapeti.Flow.Default } - public void Assign(FlowState value) - { - Metadata = value.Metadata.Clone(); - Data = value.Data; - Continuations = value.Continuations.ToDictionary(kv => kv.Key, kv => kv.Value.Clone()); - } - - public FlowState Clone() { - var result = new FlowState(); - result.Assign(this); - - return result; + return new FlowState { + metadata = metadata.Clone(), + Data = Data, + continuations = continuations?.ToDictionary(kv => kv.Key, kv => kv.Value.Clone()) + }; } } diff --git a/Tapeti.Flow/Default/FlowStore.cs b/Tapeti.Flow/Default/FlowStore.cs index b4e63ba..d2a6d87 100644 --- a/Tapeti.Flow/Default/FlowStore.cs +++ b/Tapeti.Flow/Default/FlowStore.cs @@ -5,16 +5,19 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Tapeti.Flow.FlowHelpers; namespace Tapeti.Flow.Default { public class FlowStore : IFlowStore { - private static readonly ConcurrentDictionary FlowStates = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary ContinuationLookup = new ConcurrentDictionary(); + private readonly ConcurrentDictionary FlowStates = new ConcurrentDictionary(); + private readonly ConcurrentDictionary ContinuationLookup = new ConcurrentDictionary(); + private readonly LockCollection Locks = new LockCollection(EqualityComparer.Default); private readonly IFlowRepository repository; + private volatile bool InUse = false; public FlowStore(IFlowRepository repository) { @@ -24,6 +27,11 @@ namespace Tapeti.Flow.Default public async Task Load() { + if (InUse) + throw new InvalidOperationException("Can only load the saved state once."); + + InUse = true; + FlowStates.Clear(); ContinuationLookup.Clear(); @@ -46,97 +54,76 @@ namespace Tapeti.Flow.Default public async Task LockFlowState(Guid flowID) { - var isNew = false; - var flowState = FlowStates.GetOrAdd(flowID, id => - { - isNew = true; - return new FlowState(); - }); + InUse = true; - var result = new FlowStateLock(this, flowState, flowID, isNew); - await result.Lock(); - - return result; + var flowStatelock = new FlowStateLock(this, flowID, await Locks.GetLock(flowID)); + return flowStatelock; } - private class FlowStateLock : IFlowStateLock { - private readonly SemaphoreSlim semaphore = new SemaphoreSlim(1); - private readonly FlowStore owner; - private readonly FlowState flowState; private readonly Guid flowID; - private bool isNew; - private bool isDisposed; + private volatile IDisposable flowLock; + private FlowState flowState; - public FlowStateLock(FlowStore owner, FlowState flowState, Guid flowID, bool isNew) + public FlowStateLock(FlowStore owner, Guid flowID, IDisposable flowLock) { this.owner = owner; - this.flowState = flowState; this.flowID = flowID; - this.isNew = isNew; + this.flowLock = flowLock; + + owner.FlowStates.TryGetValue(flowID, out flowState); } - - public Task Lock() - { - return semaphore.WaitAsync(); - } - - public void Dispose() { - lock (flowState) - { - if (!isDisposed) - { - semaphore.Release(); - semaphore.Dispose(); - } - - isDisposed = true; - } + var l = flowLock; + flowLock = null; + l?.Dispose(); } public Guid FlowID => flowID; public Task GetFlowState() { - lock (flowState) - { - if (isDisposed) - throw new ObjectDisposedException("FlowStateLock"); + if (flowLock == null) + throw new ObjectDisposedException("FlowStateLock"); - return Task.FromResult(flowState.Clone()); - } + return Task.FromResult(flowState?.Clone()); } public async Task StoreFlowState(FlowState newFlowState) { - lock (flowState) - { - if (isDisposed) - throw new ObjectDisposedException("FlowStateLock"); + if (flowLock == null) + throw new ObjectDisposedException("FlowStateLock"); + // Ensure no one has a direct reference to the protected state in the dictionary + newFlowState = newFlowState.Clone(); + + // Update the lookup dictionary for the ContinuationIDs + if (flowState != null) + { foreach (var removedContinuation in flowState.Continuations.Keys.Where(k => !newFlowState.Continuations.ContainsKey(k))) { Guid removedValue; - ContinuationLookup.TryRemove(removedContinuation, out removedValue); + owner.ContinuationLookup.TryRemove(removedContinuation, out removedValue); } - - foreach (var addedContinuation in newFlowState.Continuations.Where(c => !flowState.Continuations.ContainsKey(c.Key))) - { - ContinuationLookup.TryAdd(addedContinuation.Key, flowID); - } - - flowState.Assign(newFlowState); } + foreach (var addedContinuation in newFlowState.Continuations.Where(c => flowState == null || !flowState.Continuations.ContainsKey(c.Key))) + { + owner.ContinuationLookup.TryAdd(addedContinuation.Key, flowID); + } + + var isNew = flowState == null; + flowState = newFlowState; + owner.FlowStates[flowID] = newFlowState; + + // Storing the flowstate in the underlying repository if (isNew) { - isNew = false; var now = DateTime.UtcNow; await owner.repository.CreateState(flowID, flowState, now); } @@ -148,50 +135,27 @@ namespace Tapeti.Flow.Default public async Task DeleteFlowState() { - lock (flowState) - { - if (isDisposed) - throw new ObjectDisposedException("FlowStateLock"); + if (flowLock == null) + throw new ObjectDisposedException("FlowStateLock"); + if (flowState != null) + { foreach (var removedContinuation in flowState.Continuations.Keys) { Guid removedValue; - ContinuationLookup.TryRemove(removedContinuation, out removedValue); + owner.ContinuationLookup.TryRemove(removedContinuation, out removedValue); } FlowState removedFlow; - FlowStates.TryRemove(flowID, out removedFlow); + owner.FlowStates.TryRemove(flowID, out removedFlow); + + if (flowState != null) + { + flowState = null; + await owner.repository.DeleteState(flowID); + } } - - if (!isNew) - await owner.repository.DeleteState(flowID); } } - - - private static FlowStateRecord ToFlowStateRecord(Guid flowID, FlowState flowState) - { - return new FlowStateRecord - { - FlowID = flowID, - Metadata = JsonConvert.SerializeObject(flowState.Metadata), - Data = flowState.Data, - ContinuationMetadata = flowState.Continuations.ToDictionary( - kv => kv.Key, - kv => JsonConvert.SerializeObject(kv.Value)) - }; - } - - private static FlowState ToFlowState(FlowStateRecord flowStateRecord) - { - return new FlowState - { - Metadata = JsonConvert.DeserializeObject(flowStateRecord.Metadata), - Data = flowStateRecord.Data, - Continuations = flowStateRecord.ContinuationMetadata.ToDictionary( - kv => kv.Key, - kv => JsonConvert.DeserializeObject(kv.Value)) - }; - } } } diff --git a/Tapeti.Flow/Default/IExecutableYieldPoint.cs b/Tapeti.Flow/Default/IExecutableYieldPoint.cs deleted file mode 100644 index 80db564..0000000 --- a/Tapeti.Flow/Default/IExecutableYieldPoint.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Threading.Tasks; - -namespace Tapeti.Flow.Default -{ - internal interface IExecutableYieldPoint : IYieldPoint - { - bool StoreState { get; } - Task Execute(FlowContext context); - } -} diff --git a/Tapeti.Flow/Default/StateYieldPoint.cs b/Tapeti.Flow/Default/StateYieldPoint.cs deleted file mode 100644 index 521c2fc..0000000 --- a/Tapeti.Flow/Default/StateYieldPoint.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Threading.Tasks; - -namespace Tapeti.Flow.Default -{ - internal class StateYieldPoint : IExecutableYieldPoint - { - public bool StoreState { get; } - - - public StateYieldPoint(bool storeState) - { - StoreState = storeState; - } - - - public async Task Execute(FlowContext context) - { - if (StoreState) - await context.EnsureStored(); - } - } -} diff --git a/Tapeti.Flow/FlowHelpers/LockCollection.cs b/Tapeti.Flow/FlowHelpers/LockCollection.cs new file mode 100644 index 0000000..f8a3533 --- /dev/null +++ b/Tapeti.Flow/FlowHelpers/LockCollection.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tapeti.Flow.FlowHelpers +{ + public class LockCollection + { + private readonly Dictionary locks; + + public LockCollection(IEqualityComparer comparer) + { + locks = new Dictionary(comparer); + } + + public Task GetLock(T key) + { + LockItem nextLi = new LockItem(locks, key); + try + { + bool continueImmediately = false; + lock (locks) + { + LockItem li; + if (!locks.TryGetValue(key, out li)) + { + locks.Add(key, nextLi); + continueImmediately = true; + } + else + { + while (li.Next != null) + li = li.Next; + + li.Next = nextLi; + } + } + if (continueImmediately) + nextLi.Continue(); + } + catch (Exception e) + { + nextLi.Error(e); + } + return nextLi.GetTask(); + } + + private class LockItem : IDisposable + { + internal volatile LockItem Next; + + private readonly Dictionary locks; + private readonly TaskCompletionSource tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + private readonly T key; + + public LockItem(Dictionary locks, T key) + { + this.locks = locks; + this.key = key; + } + + internal void Continue() + { + tcs.TrySetResult(this); + } + + internal void Error(Exception e) + { + tcs.SetException(e); + } + + internal Task GetTask() + { + return tcs.Task; + } + + public void Dispose() + { + lock (locks) + { + LockItem li; + if (!locks.TryGetValue(key, out li)) + return; + + if (li != this) + { + // Something is wrong (comparer is not stable?), but we cannot loose the completions sources + while (li.Next != null) + li = li.Next; + li.Next = Next; + return; + } + + if (Next == null) + { + locks.Remove(key); + return; + } + + locks[key] = Next; + } + + Next.Continue(); + } + } + } + +} diff --git a/Tapeti.Flow/FlowMiddleware.cs b/Tapeti.Flow/FlowMiddleware.cs index fb13d90..751374c 100644 --- a/Tapeti.Flow/FlowMiddleware.cs +++ b/Tapeti.Flow/FlowMiddleware.cs @@ -18,8 +18,8 @@ namespace Tapeti.Flow container.RegisterDefault(); container.RegisterDefault(); container.RegisterDefault(); - container.RegisterDefault(() => flowRepository ?? new NonPersistentFlowRepository()); - container.RegisterDefault(); + container.RegisterDefaultSingleton(() => flowRepository ?? new NonPersistentFlowRepository()); + container.RegisterDefaultSingleton(); } public IEnumerable GetMiddleware(IDependencyResolver dependencyResolver) diff --git a/Tapeti.Flow/Tapeti.Flow.csproj b/Tapeti.Flow/Tapeti.Flow.csproj index 5a4b9fd..b4cb237 100644 --- a/Tapeti.Flow/Tapeti.Flow.csproj +++ b/Tapeti.Flow/Tapeti.Flow.csproj @@ -61,11 +61,10 @@ - - + diff --git a/Test/MarcoController.cs b/Test/MarcoController.cs index a3a65f4..4e631b2 100644 --- a/Test/MarcoController.cs +++ b/Test/MarcoController.cs @@ -19,6 +19,7 @@ namespace Test // Public properties are automatically stored and retrieved while in a flow public Guid StateTestGuid { get; set; } + public int Phase; public MarcoController(IPublisher publisher, IFlowProvider flowProvider, Visualizer visualizer) { @@ -31,15 +32,17 @@ namespace Test [Start] public async Task StartFlow(bool go) { - Console.WriteLine("Starting stand-alone flow"); - await Task.Delay(1000); + Console.WriteLine("Phase = " + Phase + " Starting stand-alone flow"); + await Task.Delay(10); + + Phase = 1; if (go) return flowProvider.YieldWithRequestSync (new PoloConfirmationRequestMessage(), HandlePoloConfirmationResponse); - Console.WriteLine("Ending stand-alone flow prematurely"); + Console.WriteLine("Phase = " + Phase + " Ending stand-alone flow prematurely"); return flowProvider.End(); } @@ -47,7 +50,21 @@ namespace Test [Continuation] public IYieldPoint HandlePoloConfirmationResponse(PoloConfirmationResponseMessage msg) { - Console.WriteLine("Ending stand-alone flow"); + Console.WriteLine("Phase = " + Phase + " Handling the first response and sending the second..."); + + Phase = 2; + + return flowProvider.YieldWithRequestSync + (new PoloConfirmationRequestMessage(), + HandlePoloConfirmationResponseEnd); + + } + + + [Continuation] + public IYieldPoint HandlePoloConfirmationResponseEnd(PoloConfirmationResponseMessage msg) + { + Console.WriteLine("Phase = " + Phase + " Handling the second response and Ending stand-alone flow"); return flowProvider.End(); } diff --git a/Test/MarcoEmitter.cs b/Test/MarcoEmitter.cs index 7298937..66a4a75 100644 --- a/Test/MarcoEmitter.cs +++ b/Test/MarcoEmitter.cs @@ -17,7 +17,7 @@ namespace Test public async Task Run() { - await publisher.Publish(new MarcoMessage()); + //await publisher.Publish(new MarcoMessage()); /* var concurrent = new SemaphoreSlim(20); diff --git a/Test/Program.cs b/Test/Program.cs index dc9fef8..9a71579 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -7,6 +7,7 @@ using Tapeti.Flow; using Tapeti.Flow.SQL; using Tapeti.Helpers; using Tapeti.SimpleInjector; +using System.Threading; namespace Test { @@ -36,6 +37,11 @@ namespace Test Params = new TapetiAppSettingsConnectionParams() }) { + var flowStore = container.GetInstance(); + var flowStore2 = container.GetInstance(); + + Console.WriteLine("IFlowHandler is singleton = " + (flowStore == flowStore2)); + connection.Connected += (sender, e) => { Console.WriteLine("Event Connected"); }; @@ -58,6 +64,8 @@ namespace Test container.GetInstance().Start(c => c.StartFlow, true); + Thread.Sleep(1000); + var emitter = container.GetInstance(); emitter.Run().Wait(); From ee86e2c7393221e2afc2e3cd6479ac8dda4b21ad Mon Sep 17 00:00:00 2001 From: Menno van Lavieren Date: Tue, 17 Oct 2017 13:29:16 +0200 Subject: [PATCH 09/20] RDB-136 Flow tabel wordt niet meer opgeruimd Bij een exceptie in Flow Refactoring om de actie van de Exceptionstrategie door te geven aan de cleanup stack --- Tapeti.Flow/Default/FlowCleanupMiddleware.cs | 5 +- Tapeti.Flow/Default/FlowStarter.cs | 13 ++-- Tapeti/Config/ICleanupMiddleware.cs | 2 +- Tapeti/Config/IExceptionStrategyContext.cs | 17 +++++ Tapeti/Connection/TapetiConsumer.cs | 38 ++++++++-- Tapeti/Default/ExceptionStrategyContext.cs | 40 ++++++++++ Tapeti/Default/NackExceptionStrategy.cs | 17 +++++ Tapeti/Default/RequeueExceptionStrategy.cs | 5 +- Tapeti/HandlingResult.cs | 79 ++++++++++++++++++++ Tapeti/IExceptionStrategy.cs | 8 +- Tapeti/MessageFutureAction.cs | 15 ++++ Tapeti/Tapeti.csproj | 5 ++ Tapeti/TapetiConfig.cs | 2 +- Test/MarcoController.cs | 1 - 14 files changed, 223 insertions(+), 24 deletions(-) create mode 100644 Tapeti/Config/IExceptionStrategyContext.cs create mode 100644 Tapeti/Default/ExceptionStrategyContext.cs create mode 100644 Tapeti/Default/NackExceptionStrategy.cs create mode 100644 Tapeti/HandlingResult.cs create mode 100644 Tapeti/MessageFutureAction.cs diff --git a/Tapeti.Flow/Default/FlowCleanupMiddleware.cs b/Tapeti.Flow/Default/FlowCleanupMiddleware.cs index 811be59..16cf61b 100644 --- a/Tapeti.Flow/Default/FlowCleanupMiddleware.cs +++ b/Tapeti.Flow/Default/FlowCleanupMiddleware.cs @@ -9,7 +9,7 @@ namespace Tapeti.Flow.Default { public class FlowCleanupMiddleware : ICleanupMiddleware { - public async Task Handle(IMessageContext context, ConsumeResponse response) + public async Task Handle(IMessageContext context, HandlingResult handlingResult) { object flowContextObj; if (!context.Items.TryGetValue(ContextItems.FlowContext, out flowContextObj)) @@ -18,7 +18,8 @@ namespace Tapeti.Flow.Default if (flowContext?.FlowStateLock != null) { - if (response == ConsumeResponse.Nack) + if (handlingResult.ConsumeResponse == ConsumeResponse.Nack + || handlingResult.MessageAction == MessageAction.ErrorLog) { await flowContext.FlowStateLock.DeleteFlowState(); } diff --git a/Tapeti.Flow/Default/FlowStarter.cs b/Tapeti.Flow/Default/FlowStarter.cs index d376f65..306f034 100644 --- a/Tapeti.Flow/Default/FlowStarter.cs +++ b/Tapeti.Flow/Default/FlowStarter.cs @@ -55,25 +55,28 @@ namespace Tapeti.Flow.Default var flowHandler = config.DependencyResolver.Resolve(); - ConsumeResponse response = ConsumeResponse.Nack; + HandlingResultBuilder handlingResult = new HandlingResultBuilder + { + ConsumeResponse = ConsumeResponse.Nack, + }; try { await flowHandler.Execute(context, yieldPoint); - response = ConsumeResponse.Ack; + handlingResult.ConsumeResponse = ConsumeResponse.Ack; } finally { - await RunCleanup(context, response); + await RunCleanup(context, handlingResult.ToHandlingResult()); } } - private async Task RunCleanup(MessageContext context, ConsumeResponse response) + private async Task RunCleanup(MessageContext context, HandlingResult handlingResult) { foreach (var handler in config.CleanupMiddleware) { try { - await handler.Handle(context, response); + await handler.Handle(context, handlingResult); } catch (Exception eCleanup) { diff --git a/Tapeti/Config/ICleanupMiddleware.cs b/Tapeti/Config/ICleanupMiddleware.cs index e056ad0..290236e 100644 --- a/Tapeti/Config/ICleanupMiddleware.cs +++ b/Tapeti/Config/ICleanupMiddleware.cs @@ -8,6 +8,6 @@ namespace Tapeti.Config { public interface ICleanupMiddleware { - Task Handle(IMessageContext context, ConsumeResponse response); + Task Handle(IMessageContext context, HandlingResult handlingResult); } } diff --git a/Tapeti/Config/IExceptionStrategyContext.cs b/Tapeti/Config/IExceptionStrategyContext.cs new file mode 100644 index 0000000..2a99af9 --- /dev/null +++ b/Tapeti/Config/IExceptionStrategyContext.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tapeti.Config +{ + public interface IExceptionStrategyContext + { + IMessageContext MessageContext { get; } + + Exception Exception { get; } + + HandlingResultBuilder HandlingResult { get; set; } + } +} diff --git a/Tapeti/Connection/TapetiConsumer.cs b/Tapeti/Connection/TapetiConsumer.cs index 3beb9ed..03355f5 100644 --- a/Tapeti/Connection/TapetiConsumer.cs +++ b/Tapeti/Connection/TapetiConsumer.cs @@ -44,7 +44,7 @@ namespace Tapeti.Connection { ExceptionDispatchInfo exception = null; MessageContext context = null; - ConsumeResponse response = ConsumeResponse.Nack; + HandlingResult handlingResult = null; try { try @@ -59,7 +59,11 @@ namespace Tapeti.Connection await DispatchMesage(context, body); - response = ConsumeResponse.Ack; + handlingResult = new HandlingResult + { + ConsumeResponse = ConsumeResponse.Ack, + MessageAction = MessageAction.None + }; } catch (Exception eDispatch) { @@ -67,7 +71,11 @@ namespace Tapeti.Connection logger.HandlerException(eDispatch); try { - response = exceptionStrategy.HandleException(null, exception.SourceException); + var exceptionStrategyContext = new ExceptionStrategyContext(context, exception.SourceException); + + exceptionStrategy.HandleException(exceptionStrategyContext); + + handlingResult = exceptionStrategyContext.HandlingResult.ToHandlingResult(); } catch (Exception eStrategy) { @@ -76,7 +84,15 @@ namespace Tapeti.Connection } try { - await RunCleanup(context, response); + if (handlingResult == null) + { + handlingResult = new HandlingResult + { + ConsumeResponse = ConsumeResponse.Nack, + MessageAction = MessageAction.None + }; + } + await RunCleanup(context, handlingResult); } catch (Exception eCleanup) { @@ -87,7 +103,15 @@ namespace Tapeti.Connection { try { - await worker.Respond(deliveryTag, response); + if (handlingResult == null) + { + handlingResult = new HandlingResult + { + ConsumeResponse = ConsumeResponse.Nack, + MessageAction = MessageAction.None + }; + } + await worker.Respond(deliveryTag, handlingResult.ConsumeResponse); } catch (Exception eRespond) { @@ -108,13 +132,13 @@ namespace Tapeti.Connection }); } - private async Task RunCleanup(MessageContext context, ConsumeResponse response) + private async Task RunCleanup(MessageContext context, HandlingResult handlingResult) { foreach(var handler in cleanupMiddleware) { try { - await handler.Handle(context, response); + await handler.Handle(context, handlingResult); } catch (Exception eCleanup) { diff --git a/Tapeti/Default/ExceptionStrategyContext.cs b/Tapeti/Default/ExceptionStrategyContext.cs new file mode 100644 index 0000000..fc24ab3 --- /dev/null +++ b/Tapeti/Default/ExceptionStrategyContext.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tapeti.Config; + +namespace Tapeti.Default +{ + public class ExceptionStrategyContext : IExceptionStrategyContext + { + internal ExceptionStrategyContext(IMessageContext messageContext, Exception exception) + { + MessageContext = messageContext; + Exception = exception; + } + + public IMessageContext MessageContext { get; } + + public Exception Exception { get; } + + private HandlingResultBuilder handlingResult; + public HandlingResultBuilder HandlingResult + { + get + { + if (handlingResult == null) + { + handlingResult = new HandlingResultBuilder(); + } + return handlingResult; + } + + set + { + handlingResult = value; + } + } + } +} diff --git a/Tapeti/Default/NackExceptionStrategy.cs b/Tapeti/Default/NackExceptionStrategy.cs new file mode 100644 index 0000000..48babe3 --- /dev/null +++ b/Tapeti/Default/NackExceptionStrategy.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tapeti.Config; + +namespace Tapeti.Default +{ + public class NackExceptionStrategy : IExceptionStrategy + { + public void HandleException(IExceptionStrategyContext context) + { + context.HandlingResult.ConsumeResponse = ConsumeResponse.Nack; + } + } +} diff --git a/Tapeti/Default/RequeueExceptionStrategy.cs b/Tapeti/Default/RequeueExceptionStrategy.cs index 6a20ca7..afa3143 100644 --- a/Tapeti/Default/RequeueExceptionStrategy.cs +++ b/Tapeti/Default/RequeueExceptionStrategy.cs @@ -5,10 +5,9 @@ namespace Tapeti.Default { public class RequeueExceptionStrategy : IExceptionStrategy { - public ConsumeResponse HandleException(IMessageContext context, Exception exception) + public void HandleException(IExceptionStrategyContext context) { - // TODO log exception - return ConsumeResponse.Requeue; + context.HandlingResult.ConsumeResponse = ConsumeResponse.Requeue; } } } diff --git a/Tapeti/HandlingResult.cs b/Tapeti/HandlingResult.cs new file mode 100644 index 0000000..e1bb575 --- /dev/null +++ b/Tapeti/HandlingResult.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tapeti +{ + public class HandlingResult + { + public HandlingResult() + { + ConsumeResponse = ConsumeResponse.Nack; + MessageAction = MessageAction.None; + } + + /// + /// Determines which response will be given to the message bus from where the message originates. + /// + public ConsumeResponse ConsumeResponse { get; internal set; } + + /// + /// Registers which action the Exception strategy has taken or will take to handle the error condition + /// on the message. This is important to know for cleanup handlers registered by middleware. + /// + public MessageAction MessageAction { get; internal set; } + + } + + public class HandlingResultBuilder + { + private static readonly HandlingResult Default = new HandlingResult(); + + private HandlingResult data = Default; + + public ConsumeResponse ConsumeResponse { + get + { + return data.ConsumeResponse; + } + set + { + GetWritableData().ConsumeResponse = value; + } + } + + public MessageAction MessageAction + { + get + { + return data.MessageAction; + } + set + { + GetWritableData().MessageAction = value; + } + } + + public HandlingResult ToHandlingResult() + { + if (data == Default) + { + return new HandlingResult(); + } + var result = GetWritableData(); + data = Default; + return result; + } + + private HandlingResult GetWritableData() + { + if (data == Default) + { + data = new HandlingResult(); + } + return data; + } + } +} diff --git a/Tapeti/IExceptionStrategy.cs b/Tapeti/IExceptionStrategy.cs index 7b46af6..7525324 100644 --- a/Tapeti/IExceptionStrategy.cs +++ b/Tapeti/IExceptionStrategy.cs @@ -8,9 +8,9 @@ namespace Tapeti /// /// Called when an exception occurs while handling a message. /// - /// The message context if available. May be null! - /// The exception instance - /// The ConsumeResponse to determine whether to requeue, dead-letter (nack) or simply ack the message. - ConsumeResponse HandleException(IMessageContext context, Exception exception); + /// The exception strategy context containing the necessary data including the message context and the thrown exception. + /// Also the response to the message can be set. + /// If there is any other handling of the message than the expected default than HandlingResult.MessageFutureAction must be set accordingly. + void HandleException(IExceptionStrategyContext context); } } diff --git a/Tapeti/MessageFutureAction.cs b/Tapeti/MessageFutureAction.cs new file mode 100644 index 0000000..7cbd319 --- /dev/null +++ b/Tapeti/MessageFutureAction.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tapeti +{ + public enum MessageAction + { + None = 1, + ErrorLog = 2, + Retry = 3, + } +} diff --git a/Tapeti/Tapeti.csproj b/Tapeti/Tapeti.csproj index f41e903..5ce8b98 100644 --- a/Tapeti/Tapeti.csproj +++ b/Tapeti/Tapeti.csproj @@ -53,6 +53,7 @@ + @@ -64,6 +65,9 @@ + + + @@ -84,6 +88,7 @@ + diff --git a/Tapeti/TapetiConfig.cs b/Tapeti/TapetiConfig.cs index b8ed17b..117c4c9 100644 --- a/Tapeti/TapetiConfig.cs +++ b/Tapeti/TapetiConfig.cs @@ -143,7 +143,7 @@ namespace Tapeti container.RegisterDefault(); container.RegisterDefault(); container.RegisterDefault(); - container.RegisterDefault(); + container.RegisterDefault(); } diff --git a/Test/MarcoController.cs b/Test/MarcoController.cs index 4e631b2..fe78c4b 100644 --- a/Test/MarcoController.cs +++ b/Test/MarcoController.cs @@ -57,7 +57,6 @@ namespace Test return flowProvider.YieldWithRequestSync (new PoloConfirmationRequestMessage(), HandlePoloConfirmationResponseEnd); - } From be7637e784bd821c01ea0c6574fb2c03118cc9c5 Mon Sep 17 00:00:00 2001 From: Menno van Lavieren Date: Tue, 17 Oct 2017 13:29:16 +0200 Subject: [PATCH 10/20] RDB-136 Flow tabel wordt niet meer opgeruimd Bij een exceptie in Flow Refactoring om de actie van de Exceptionstrategie door te geven aan de cleanup stack Megre bug geresolved --- Tapeti.Flow/Default/FlowCleanupMiddleware.cs | 5 +- Tapeti.Flow/Default/FlowStarter.cs | 29 ++++++- Tapeti/Config/ICleanupMiddleware.cs | 2 +- Tapeti/Config/IExceptionStrategyContext.cs | 17 +++++ Tapeti/Connection/TapetiConsumer.cs | 38 ++++++++-- Tapeti/Default/ExceptionStrategyContext.cs | 40 ++++++++++ Tapeti/Default/NackExceptionStrategy.cs | 17 +++++ Tapeti/Default/RequeueExceptionStrategy.cs | 5 +- Tapeti/HandlingResult.cs | 79 ++++++++++++++++++++ Tapeti/IExceptionStrategy.cs | 8 +- Tapeti/MessageFutureAction.cs | 15 ++++ Tapeti/Tapeti.csproj | 5 ++ Tapeti/TapetiConfig.cs | 2 +- 13 files changed, 243 insertions(+), 19 deletions(-) create mode 100644 Tapeti/Config/IExceptionStrategyContext.cs create mode 100644 Tapeti/Default/ExceptionStrategyContext.cs create mode 100644 Tapeti/Default/NackExceptionStrategy.cs create mode 100644 Tapeti/HandlingResult.cs create mode 100644 Tapeti/MessageFutureAction.cs diff --git a/Tapeti.Flow/Default/FlowCleanupMiddleware.cs b/Tapeti.Flow/Default/FlowCleanupMiddleware.cs index e0420c8..6ac310b 100644 --- a/Tapeti.Flow/Default/FlowCleanupMiddleware.cs +++ b/Tapeti.Flow/Default/FlowCleanupMiddleware.cs @@ -9,7 +9,7 @@ namespace Tapeti.Flow.Default { public class FlowCleanupMiddleware : ICleanupMiddleware { - public async Task Handle(IMessageContext context, ConsumeResponse response) + public async Task Handle(IMessageContext context, HandlingResult handlingResult) { object flowContextObj; if (!context.Items.TryGetValue(ContextItems.FlowContext, out flowContextObj)) @@ -18,7 +18,8 @@ namespace Tapeti.Flow.Default if (flowContext.FlowStateLock != null) { - if (response == ConsumeResponse.Nack) + if (handlingResult.ConsumeResponse == ConsumeResponse.Nack + || handlingResult.MessageAction == MessageAction.ErrorLog) { await flowContext.FlowStateLock.DeleteFlowState(); } diff --git a/Tapeti.Flow/Default/FlowStarter.cs b/Tapeti.Flow/Default/FlowStarter.cs index 215decb..eedd1e4 100644 --- a/Tapeti.Flow/Default/FlowStarter.cs +++ b/Tapeti.Flow/Default/FlowStarter.cs @@ -52,7 +52,34 @@ namespace Tapeti.Flow.Default }; var flowHandler = config.DependencyResolver.Resolve(); - await flowHandler.Execute(context, yieldPoint); + HandlingResultBuilder handlingResult = new HandlingResultBuilder + { + ConsumeResponse = ConsumeResponse.Nack, + }; + try + { + await flowHandler.Execute(context, yieldPoint); + handlingResult.ConsumeResponse = ConsumeResponse.Ack; + } + finally + { + await RunCleanup(context, handlingResult.ToHandlingResult()); + } + } + + private async Task RunCleanup(MessageContext context, HandlingResult handlingResult) + { + foreach (var handler in config.CleanupMiddleware) + { + try + { + await handler.Handle(context, handlingResult); + } + catch (Exception eCleanup) + { + logger.HandlerException(eCleanup); + } + } } diff --git a/Tapeti/Config/ICleanupMiddleware.cs b/Tapeti/Config/ICleanupMiddleware.cs index e056ad0..290236e 100644 --- a/Tapeti/Config/ICleanupMiddleware.cs +++ b/Tapeti/Config/ICleanupMiddleware.cs @@ -8,6 +8,6 @@ namespace Tapeti.Config { public interface ICleanupMiddleware { - Task Handle(IMessageContext context, ConsumeResponse response); + Task Handle(IMessageContext context, HandlingResult handlingResult); } } diff --git a/Tapeti/Config/IExceptionStrategyContext.cs b/Tapeti/Config/IExceptionStrategyContext.cs new file mode 100644 index 0000000..2a99af9 --- /dev/null +++ b/Tapeti/Config/IExceptionStrategyContext.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tapeti.Config +{ + public interface IExceptionStrategyContext + { + IMessageContext MessageContext { get; } + + Exception Exception { get; } + + HandlingResultBuilder HandlingResult { get; set; } + } +} diff --git a/Tapeti/Connection/TapetiConsumer.cs b/Tapeti/Connection/TapetiConsumer.cs index 3beb9ed..03355f5 100644 --- a/Tapeti/Connection/TapetiConsumer.cs +++ b/Tapeti/Connection/TapetiConsumer.cs @@ -44,7 +44,7 @@ namespace Tapeti.Connection { ExceptionDispatchInfo exception = null; MessageContext context = null; - ConsumeResponse response = ConsumeResponse.Nack; + HandlingResult handlingResult = null; try { try @@ -59,7 +59,11 @@ namespace Tapeti.Connection await DispatchMesage(context, body); - response = ConsumeResponse.Ack; + handlingResult = new HandlingResult + { + ConsumeResponse = ConsumeResponse.Ack, + MessageAction = MessageAction.None + }; } catch (Exception eDispatch) { @@ -67,7 +71,11 @@ namespace Tapeti.Connection logger.HandlerException(eDispatch); try { - response = exceptionStrategy.HandleException(null, exception.SourceException); + var exceptionStrategyContext = new ExceptionStrategyContext(context, exception.SourceException); + + exceptionStrategy.HandleException(exceptionStrategyContext); + + handlingResult = exceptionStrategyContext.HandlingResult.ToHandlingResult(); } catch (Exception eStrategy) { @@ -76,7 +84,15 @@ namespace Tapeti.Connection } try { - await RunCleanup(context, response); + if (handlingResult == null) + { + handlingResult = new HandlingResult + { + ConsumeResponse = ConsumeResponse.Nack, + MessageAction = MessageAction.None + }; + } + await RunCleanup(context, handlingResult); } catch (Exception eCleanup) { @@ -87,7 +103,15 @@ namespace Tapeti.Connection { try { - await worker.Respond(deliveryTag, response); + if (handlingResult == null) + { + handlingResult = new HandlingResult + { + ConsumeResponse = ConsumeResponse.Nack, + MessageAction = MessageAction.None + }; + } + await worker.Respond(deliveryTag, handlingResult.ConsumeResponse); } catch (Exception eRespond) { @@ -108,13 +132,13 @@ namespace Tapeti.Connection }); } - private async Task RunCleanup(MessageContext context, ConsumeResponse response) + private async Task RunCleanup(MessageContext context, HandlingResult handlingResult) { foreach(var handler in cleanupMiddleware) { try { - await handler.Handle(context, response); + await handler.Handle(context, handlingResult); } catch (Exception eCleanup) { diff --git a/Tapeti/Default/ExceptionStrategyContext.cs b/Tapeti/Default/ExceptionStrategyContext.cs new file mode 100644 index 0000000..fc24ab3 --- /dev/null +++ b/Tapeti/Default/ExceptionStrategyContext.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tapeti.Config; + +namespace Tapeti.Default +{ + public class ExceptionStrategyContext : IExceptionStrategyContext + { + internal ExceptionStrategyContext(IMessageContext messageContext, Exception exception) + { + MessageContext = messageContext; + Exception = exception; + } + + public IMessageContext MessageContext { get; } + + public Exception Exception { get; } + + private HandlingResultBuilder handlingResult; + public HandlingResultBuilder HandlingResult + { + get + { + if (handlingResult == null) + { + handlingResult = new HandlingResultBuilder(); + } + return handlingResult; + } + + set + { + handlingResult = value; + } + } + } +} diff --git a/Tapeti/Default/NackExceptionStrategy.cs b/Tapeti/Default/NackExceptionStrategy.cs new file mode 100644 index 0000000..48babe3 --- /dev/null +++ b/Tapeti/Default/NackExceptionStrategy.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tapeti.Config; + +namespace Tapeti.Default +{ + public class NackExceptionStrategy : IExceptionStrategy + { + public void HandleException(IExceptionStrategyContext context) + { + context.HandlingResult.ConsumeResponse = ConsumeResponse.Nack; + } + } +} diff --git a/Tapeti/Default/RequeueExceptionStrategy.cs b/Tapeti/Default/RequeueExceptionStrategy.cs index 6a20ca7..afa3143 100644 --- a/Tapeti/Default/RequeueExceptionStrategy.cs +++ b/Tapeti/Default/RequeueExceptionStrategy.cs @@ -5,10 +5,9 @@ namespace Tapeti.Default { public class RequeueExceptionStrategy : IExceptionStrategy { - public ConsumeResponse HandleException(IMessageContext context, Exception exception) + public void HandleException(IExceptionStrategyContext context) { - // TODO log exception - return ConsumeResponse.Requeue; + context.HandlingResult.ConsumeResponse = ConsumeResponse.Requeue; } } } diff --git a/Tapeti/HandlingResult.cs b/Tapeti/HandlingResult.cs new file mode 100644 index 0000000..e1bb575 --- /dev/null +++ b/Tapeti/HandlingResult.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tapeti +{ + public class HandlingResult + { + public HandlingResult() + { + ConsumeResponse = ConsumeResponse.Nack; + MessageAction = MessageAction.None; + } + + /// + /// Determines which response will be given to the message bus from where the message originates. + /// + public ConsumeResponse ConsumeResponse { get; internal set; } + + /// + /// Registers which action the Exception strategy has taken or will take to handle the error condition + /// on the message. This is important to know for cleanup handlers registered by middleware. + /// + public MessageAction MessageAction { get; internal set; } + + } + + public class HandlingResultBuilder + { + private static readonly HandlingResult Default = new HandlingResult(); + + private HandlingResult data = Default; + + public ConsumeResponse ConsumeResponse { + get + { + return data.ConsumeResponse; + } + set + { + GetWritableData().ConsumeResponse = value; + } + } + + public MessageAction MessageAction + { + get + { + return data.MessageAction; + } + set + { + GetWritableData().MessageAction = value; + } + } + + public HandlingResult ToHandlingResult() + { + if (data == Default) + { + return new HandlingResult(); + } + var result = GetWritableData(); + data = Default; + return result; + } + + private HandlingResult GetWritableData() + { + if (data == Default) + { + data = new HandlingResult(); + } + return data; + } + } +} diff --git a/Tapeti/IExceptionStrategy.cs b/Tapeti/IExceptionStrategy.cs index 7b46af6..7525324 100644 --- a/Tapeti/IExceptionStrategy.cs +++ b/Tapeti/IExceptionStrategy.cs @@ -8,9 +8,9 @@ namespace Tapeti /// /// Called when an exception occurs while handling a message. /// - /// The message context if available. May be null! - /// The exception instance - /// The ConsumeResponse to determine whether to requeue, dead-letter (nack) or simply ack the message. - ConsumeResponse HandleException(IMessageContext context, Exception exception); + /// The exception strategy context containing the necessary data including the message context and the thrown exception. + /// Also the response to the message can be set. + /// If there is any other handling of the message than the expected default than HandlingResult.MessageFutureAction must be set accordingly. + void HandleException(IExceptionStrategyContext context); } } diff --git a/Tapeti/MessageFutureAction.cs b/Tapeti/MessageFutureAction.cs new file mode 100644 index 0000000..7cbd319 --- /dev/null +++ b/Tapeti/MessageFutureAction.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tapeti +{ + public enum MessageAction + { + None = 1, + ErrorLog = 2, + Retry = 3, + } +} diff --git a/Tapeti/Tapeti.csproj b/Tapeti/Tapeti.csproj index f41e903..5ce8b98 100644 --- a/Tapeti/Tapeti.csproj +++ b/Tapeti/Tapeti.csproj @@ -53,6 +53,7 @@ + @@ -64,6 +65,9 @@ + + + @@ -84,6 +88,7 @@ + diff --git a/Tapeti/TapetiConfig.cs b/Tapeti/TapetiConfig.cs index b8ed17b..117c4c9 100644 --- a/Tapeti/TapetiConfig.cs +++ b/Tapeti/TapetiConfig.cs @@ -143,7 +143,7 @@ namespace Tapeti container.RegisterDefault(); container.RegisterDefault(); container.RegisterDefault(); - container.RegisterDefault(); + container.RegisterDefault(); } From 9b0984ec5a2d0b7aef071b2ec5f14040c5f7cd9b Mon Sep 17 00:00:00 2001 From: Menno van Lavieren Date: Tue, 17 Oct 2017 13:46:06 +0200 Subject: [PATCH 11/20] RDB-136 Flow tabel wordt niet meer opgeruimd Bij een exceptie in Flow Refactoring om de actie van de Exceptionstrategie door te geven aan de cleanup stack Deel 2 Megre bug geresolved --- Tapeti.Flow/Default/FlowStarter.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tapeti.Flow/Default/FlowStarter.cs b/Tapeti.Flow/Default/FlowStarter.cs index eedd1e4..69adc53 100644 --- a/Tapeti.Flow/Default/FlowStarter.cs +++ b/Tapeti.Flow/Default/FlowStarter.cs @@ -10,11 +10,13 @@ namespace Tapeti.Flow.Default public class FlowStarter : IFlowStarter { private readonly IConfig config; + private readonly ILogger logger; - public FlowStarter(IConfig config) + public FlowStarter(IConfig config, ILogger logger) { this.config = config; + this.logger = logger; } From 65dddb8a7496bc050911f32f3827f36df479674f Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Tue, 17 Oct 2017 17:24:53 +0200 Subject: [PATCH 12/20] AppVeyor build implementation --- Publish.ps1 | 55 ------------------- README.md | 11 ++++ Tapeti.Annotations/Tapeti.Annotations.nuspec | 32 +++++------ .../Tapeti.DataAnnotations.nuspec | 38 ++++++------- Tapeti.Flow.SQL/Tapeti.Flow.SQL.nuspec | 40 +++++++------- Tapeti.Flow/Tapeti.Flow.nuspec | 40 +++++++------- .../Tapeti.SimpleInjector.nuspec | 38 ++++++------- Tapeti/Tapeti.nuspec | 38 ++++++------- appveyor.yml | 40 ++++++++++++++ 9 files changed, 164 insertions(+), 168 deletions(-) delete mode 100644 Publish.ps1 create mode 100644 appveyor.yml diff --git a/Publish.ps1 b/Publish.ps1 deleted file mode 100644 index 7646e36..0000000 --- a/Publish.ps1 +++ /dev/null @@ -1,55 +0,0 @@ -param([switch]$nopush) - - -function pack -{ - param([string]$project) - - Write-Host "Packing $($project).csproj" -Foreground Blue - NuGet.exe pack "$($project)\$($project).csproj" -Build -OutputDir publish -Version "$($version.NuGetVersion)" -Properties depversion="$($version.NuGetVersion)" -} - - -function push -{ - param([string]$project) - - Write-Host "Pushing $($project).csproj" -Foreground Blue - NuGet.exe push "publish\X2Software.$($project).$($version.NuGetVersion).nupkg" -apikey "$($nugetkey)" -Source https://www.nuget.org/api/v2/package -} - - -$projects = @( - "Tapeti.Annotations", - "Tapeti", - "Tapeti.DataAnnotations", - "Tapeti.Flow", - "Tapeti.SimpleInjector" -) - - -New-Item -Path publish -Type directory -Force | Out-Null - -$version = GitVersion.exe | Out-String | ConvertFrom-Json -$nugetkey = Get-Content .nuget.apikey - - -Write-Host "Publishing version $($version.NuGetVersion) using API key $($nugetkey)"-Foreground Cyan - -foreach ($project in $projects) -{ - pack($project) -} - - -if ($nopush -eq $false) -{ - foreach ($project in $projects) - { - push($project) - } -} -else -{ - Write-Host "Skipping push" -Foreground Blue -} \ No newline at end of file diff --git a/README.md b/README.md index 4d31212..2b8f6ee 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,14 @@ The documentation for Tapeti is available on Read the Docs: [Master branch](http://tapeti.readthedocs.io/en/stable/)
[![Documentation Status](https://readthedocs.org/projects/tapeti/badge/?version=stable)](http://tapeti.readthedocs.io/en/stable/?badge=stable) + + +## Builds +Builds are automatically run using AppVeyor, with the resulting packages being pushed to NuGet. + + +Latest build +[![Build status](https://ci.appveyor.com/api/projects/status/p5w9yr4xlhvoy02i?svg=true)](https://ci.appveyor.com/project/MvRens/tapeti) + +Master build +[![Build status](https://ci.appveyor.com/api/projects/status/p5w9yr4xlhvoy02i/branch/master?svg=true)](https://ci.appveyor.com/project/MvRens/tapeti/branch/master) diff --git a/Tapeti.Annotations/Tapeti.Annotations.nuspec b/Tapeti.Annotations/Tapeti.Annotations.nuspec index acceaf2..d2684ac 100644 --- a/Tapeti.Annotations/Tapeti.Annotations.nuspec +++ b/Tapeti.Annotations/Tapeti.Annotations.nuspec @@ -1,17 +1,17 @@ - - - - X2Software.Tapeti.Annotations - $version$ - $title$ - Mark van Renswoude - Mark van Renswoude - https://git.x2software.net/pub/tapeti/raw/master/UNLICENSE - https://git.x2software.net/pub/tapeti - https://git.x2software.net/pub/tapeti/raw/master/resources/icons/Tapeti.Annotations.png - false - Annotations for Tapeti - - rabbitmq tapeti - + + + + X2Software.Tapeti.Annotations + $version$ + Tapeti Annotations + Mark van Renswoude + Mark van Renswoude + https://raw.githubusercontent.com/MvRens/Tapeti/master/UNLICENSE + https://github.com/MvRens/Tapeti + https://raw.githubusercontent.com/MvRens/Tapeti/master/resources/icons/Tapeti.Annotations.png + false + Annotations for Tapeti + + rabbitmq tapeti + \ No newline at end of file diff --git a/Tapeti.DataAnnotations/Tapeti.DataAnnotations.nuspec b/Tapeti.DataAnnotations/Tapeti.DataAnnotations.nuspec index a1b9042..571c3a5 100644 --- a/Tapeti.DataAnnotations/Tapeti.DataAnnotations.nuspec +++ b/Tapeti.DataAnnotations/Tapeti.DataAnnotations.nuspec @@ -1,20 +1,20 @@ - - - - X2Software.Tapeti.DataAnnotations - $version$ - $title$ - Mark van Renswoude - Mark van Renswoude - https://git.x2software.net/pub/tapeti/raw/master/UNLICENSE - https://git.x2software.net/pub/tapeti - https://git.x2software.net/pub/tapeti/raw/master/resources/icons/Tapeti.DataAnnotations.png - false - DataAnnotations validation extension for Tapeti - - rabbitmq tapeti dataannotations - - - - + + + + X2Software.Tapeti.DataAnnotations + $version$ + Tapeti DataAnnotations + Mark van Renswoude + Mark van Renswoude + https://raw.githubusercontent.com/MvRens/Tapeti/master/UNLICENSE + https://github.com/MvRens/Tapeti + https://raw.githubusercontent.com/MvRens/Tapeti/master/resources/icons/Tapeti.DataAnnotations.png + false + DataAnnotations validation extension for Tapeti + + rabbitmq tapeti dataannotations + + + + \ No newline at end of file diff --git a/Tapeti.Flow.SQL/Tapeti.Flow.SQL.nuspec b/Tapeti.Flow.SQL/Tapeti.Flow.SQL.nuspec index 686bbf9..0a667c6 100644 --- a/Tapeti.Flow.SQL/Tapeti.Flow.SQL.nuspec +++ b/Tapeti.Flow.SQL/Tapeti.Flow.SQL.nuspec @@ -1,21 +1,21 @@ - - - - X2Software.Tapeti.Flow.SQL - $version$ - $title$ - Mark van Renswoude - Mark van Renswoude - https://git.x2software.net/pub/tapeti/raw/master/UNLICENSE - https://git.x2software.net/pub/tapeti - https://git.x2software.net/pub/tapeti/raw/master/resources/icons/Tapeti.Flow.SQL.png - false - SQL backing repository for the Tapeti Flow package - - rabbitmq tapeti sql - - - - - + + + + X2Software.Tapeti.Flow.SQL + $version$ + Tapeti Flow SQL + Mark van Renswoude + Mark van Renswoude + https://raw.githubusercontent.com/MvRens/Tapeti/master/UNLICENSE + https://github.com/MvRens/Tapeti + https://raw.githubusercontent.com/MvRens/Tapeti/master/resources/icons/Tapeti.Flow.SQL.png + false + SQL backing repository for the Tapeti Flow package + + rabbitmq tapeti sql + + + + + \ No newline at end of file diff --git a/Tapeti.Flow/Tapeti.Flow.nuspec b/Tapeti.Flow/Tapeti.Flow.nuspec index 1df4677..54a7e1f 100644 --- a/Tapeti.Flow/Tapeti.Flow.nuspec +++ b/Tapeti.Flow/Tapeti.Flow.nuspec @@ -1,21 +1,21 @@ - - - - X2Software.Tapeti.Flow - $version$ - $title$ - Menno van Lavieren, Mark van Renswoude - Mark van Renswoude - https://git.x2software.net/pub/tapeti/raw/master/UNLICENSE - https://git.x2software.net/pub/tapeti - https://git.x2software.net/pub/tapeti/raw/master/resources/icons/Tapeti.Flow.png - false - Flow extension for Tapeti - - rabbitmq tapeti flow - - - - - + + + + X2Software.Tapeti.Flow + $version$ + Tapeti Flow + Menno van Lavieren, Mark van Renswoude + Mark van Renswoude + https://raw.githubusercontent.com/MvRens/Tapeti/master/UNLICENSE + https://github.com/MvRens/Tapeti + https://raw.githubusercontent.com/MvRens/Tapeti/master/resources/icons/Tapeti.Flow.png + false + Flow extension for Tapeti + + rabbitmq tapeti flow + + + + + \ No newline at end of file diff --git a/Tapeti.SimpleInjector/Tapeti.SimpleInjector.nuspec b/Tapeti.SimpleInjector/Tapeti.SimpleInjector.nuspec index 6af6063..e53c636 100644 --- a/Tapeti.SimpleInjector/Tapeti.SimpleInjector.nuspec +++ b/Tapeti.SimpleInjector/Tapeti.SimpleInjector.nuspec @@ -1,20 +1,20 @@ - - - - X2Software.Tapeti.SimpleInjector - $version$ - $title$ - Mark van Renswoude - Mark van Renswoude - https://git.x2software.net/pub/tapeti/raw/master/UNLICENSE - https://git.x2software.net/pub/tapeti - https://git.x2software.net/pub/tapeti/raw/master/resources/icons/Tapeti.SimpleInjector.png - false - SimpleInjector integration package for Tapeti - - rabbitmq tapeti simpleinjector - - - - + + + + X2Software.Tapeti.SimpleInjector + $version$ + Tapeti SimpleInjector + Mark van Renswoude + Mark van Renswoude + https://raw.githubusercontent.com/MvRens/Tapeti/master/UNLICENSE + https://github.com/MvRens/Tapeti + https://raw.githubusercontent.com/MvRens/Tapeti/master/resources/icons/Tapeti.SimpleInjector.png + false + SimpleInjector integration package for Tapeti + + rabbitmq tapeti simpleinjector + + + + \ No newline at end of file diff --git a/Tapeti/Tapeti.nuspec b/Tapeti/Tapeti.nuspec index 249c4c2..b1eb7ca 100644 --- a/Tapeti/Tapeti.nuspec +++ b/Tapeti/Tapeti.nuspec @@ -1,20 +1,20 @@ - - - - X2Software.Tapeti - $version$ - $title$ - Mark van Renswoude - Mark van Renswoude - https://git.x2software.net/pub/tapeti/raw/master/UNLICENSE - https://git.x2software.net/pub/tapeti - https://git.x2software.net/pub/tapeti/raw/master/resources/icons/Tapeti.png - false - Controller-based framework for RabbitMQ microservice architectures - - rabbitmq tapeti - - - - + + + + X2Software.Tapeti + $version$ + Tapeti + Mark van Renswoude + Mark van Renswoude + https://raw.githubusercontent.com/MvRens/Tapeti/master/UNLICENSE + https://github.com/MvRens/Tapeti + https://raw.githubusercontent.com/MvRens/Tapeti/master/resources/icons/Tapeti.png + false + Controller-based framework for RabbitMQ microservice architectures + + rabbitmq tapeti + + + + \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..aa68da9 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,40 @@ +image: Visual Studio 2015 + +install: + - choco install gitversion.portable -pre -y + +before_build: + - nuget restore + - ps: gitversion /l console /output buildserver /updateAssemblyInfo + +after_build: + - cmd: ECHO nuget pack Tapeti\Tapeti.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" + - cmd: nuget pack Tapeti\Tapeti.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" + - cmd: appveyor PushArtifact "Tapeti.%GitVersion_NuGetVersion%.nupkg" + - cmd: nuget pack Tapeti.Annotations\Tapeti.Annotations.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" + - cmd: appveyor PushArtifact "Tapeti.Annotations.%GitVersion_NuGetVersion%.nupkg" + - cmd: nuget pack Tapeti.DataAnnotations\Tapeti.DataAnnotations.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" + - cmd: appveyor PushArtifact "Tapeti.DataAnnotations.%GitVersion_NuGetVersion%.nupkg" + - cmd: nuget pack Tapeti.Flow\Tapeti.Flow.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" + - cmd: appveyor PushArtifact "Tapeti.Flow.%GitVersion_NuGetVersion%.nupkg" + - cmd: nuget pack Tapeti.SimpleInjector\Tapeti.SimpleInjector.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" + - cmd: appveyor PushArtifact "Tapeti.SimpleInjector.%GitVersion_NuGetVersion%.nupkg" + +assembly_info: + patch: false + +build: + project: Tapeti.sln + +platform: + - Any CPU + +configuration: + - Release + +deploy: + provider: NuGet + api_key: + secure: wEch4shvYGZUNRW0z/Rm8jdKR95lVnzS0QncQg6a0wH4k/Bt6N/B5bs6AidVcyUD + skip_symbols: false + artifact: /.*\.nupkg/ \ No newline at end of file From 58fc9b30ae190fc81d020af1a74422e4f7975a2f Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Tue, 17 Oct 2017 17:27:46 +0200 Subject: [PATCH 13/20] Fixed package names for PushArtifact --- appveyor.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index aa68da9..1984f63 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,15 +10,15 @@ before_build: after_build: - cmd: ECHO nuget pack Tapeti\Tapeti.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" - cmd: nuget pack Tapeti\Tapeti.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" - - cmd: appveyor PushArtifact "Tapeti.%GitVersion_NuGetVersion%.nupkg" + - cmd: appveyor PushArtifact "X2Software.Tapeti.%GitVersion_NuGetVersion%.nupkg" - cmd: nuget pack Tapeti.Annotations\Tapeti.Annotations.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" - - cmd: appveyor PushArtifact "Tapeti.Annotations.%GitVersion_NuGetVersion%.nupkg" + - cmd: appveyor PushArtifact "X2Software.Tapeti.Annotations.%GitVersion_NuGetVersion%.nupkg" - cmd: nuget pack Tapeti.DataAnnotations\Tapeti.DataAnnotations.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" - - cmd: appveyor PushArtifact "Tapeti.DataAnnotations.%GitVersion_NuGetVersion%.nupkg" + - cmd: appveyor PushArtifact "X2Software.Tapeti.DataAnnotations.%GitVersion_NuGetVersion%.nupkg" - cmd: nuget pack Tapeti.Flow\Tapeti.Flow.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" - - cmd: appveyor PushArtifact "Tapeti.Flow.%GitVersion_NuGetVersion%.nupkg" + - cmd: appveyor PushArtifact "X2Software.Tapeti.Flow.%GitVersion_NuGetVersion%.nupkg" - cmd: nuget pack Tapeti.SimpleInjector\Tapeti.SimpleInjector.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" - - cmd: appveyor PushArtifact "Tapeti.SimpleInjector.%GitVersion_NuGetVersion%.nupkg" + - cmd: appveyor PushArtifact "X2Software.Tapeti.SimpleInjector.%GitVersion_NuGetVersion%.nupkg" assembly_info: patch: false From 5e8719719f81162669a20ad4ac278768546da904 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Tue, 17 Oct 2017 17:35:36 +0200 Subject: [PATCH 14/20] Updated NuGet API key --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 1984f63..374feac 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -35,6 +35,6 @@ configuration: deploy: provider: NuGet api_key: - secure: wEch4shvYGZUNRW0z/Rm8jdKR95lVnzS0QncQg6a0wH4k/Bt6N/B5bs6AidVcyUD + secure: pkaN6R8ocu0Q93uCK3DOCifgr1Q4tuH4ZJ4eiV9U5NmwE5qRM2xjUy4B9SkZCsWx skip_symbols: false artifact: /.*\.nupkg/ \ No newline at end of file From 53a9218a69b7ad208a1d135b75240ff62055c82a Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Tue, 17 Oct 2017 17:44:22 +0200 Subject: [PATCH 15/20] Updated build badges --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2b8f6ee..c35e158 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Builds are automatically run using AppVeyor, with the resulting packages being p Latest build -[![Build status](https://ci.appveyor.com/api/projects/status/p5w9yr4xlhvoy02i?svg=true)](https://ci.appveyor.com/project/MvRens/tapeti) +[![Build status](https://ci.appveyor.com/api/projects/status/cyuo0vm7admy0d9x?svg=true)](https://ci.appveyor.com/project/MvRens/tapeti) Master build -[![Build status](https://ci.appveyor.com/api/projects/status/p5w9yr4xlhvoy02i/branch/master?svg=true)](https://ci.appveyor.com/project/MvRens/tapeti/branch/master) +[![Build status](https://ci.appveyor.com/api/projects/status/cyuo0vm7admy0d9x/branch/master?svg=true)](https://ci.appveyor.com/project/MvRens/tapeti/branch/master) From 0924084a0061199a2e83d5bb35c208fa3253ea5f Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Tue, 17 Oct 2017 17:24:53 +0200 Subject: [PATCH 16/20] AppVeyor build implementation --- Publish.ps1 | 55 ------------------- README.md | 11 ++++ Tapeti.Annotations/Tapeti.Annotations.nuspec | 32 +++++------ .../Tapeti.DataAnnotations.nuspec | 38 ++++++------- Tapeti.Flow.SQL/Tapeti.Flow.SQL.nuspec | 40 +++++++------- Tapeti.Flow/Tapeti.Flow.nuspec | 40 +++++++------- .../Tapeti.SimpleInjector.nuspec | 38 ++++++------- Tapeti/Tapeti.nuspec | 38 ++++++------- appveyor.yml | 40 ++++++++++++++ 9 files changed, 164 insertions(+), 168 deletions(-) delete mode 100644 Publish.ps1 create mode 100644 appveyor.yml diff --git a/Publish.ps1 b/Publish.ps1 deleted file mode 100644 index 7646e36..0000000 --- a/Publish.ps1 +++ /dev/null @@ -1,55 +0,0 @@ -param([switch]$nopush) - - -function pack -{ - param([string]$project) - - Write-Host "Packing $($project).csproj" -Foreground Blue - NuGet.exe pack "$($project)\$($project).csproj" -Build -OutputDir publish -Version "$($version.NuGetVersion)" -Properties depversion="$($version.NuGetVersion)" -} - - -function push -{ - param([string]$project) - - Write-Host "Pushing $($project).csproj" -Foreground Blue - NuGet.exe push "publish\X2Software.$($project).$($version.NuGetVersion).nupkg" -apikey "$($nugetkey)" -Source https://www.nuget.org/api/v2/package -} - - -$projects = @( - "Tapeti.Annotations", - "Tapeti", - "Tapeti.DataAnnotations", - "Tapeti.Flow", - "Tapeti.SimpleInjector" -) - - -New-Item -Path publish -Type directory -Force | Out-Null - -$version = GitVersion.exe | Out-String | ConvertFrom-Json -$nugetkey = Get-Content .nuget.apikey - - -Write-Host "Publishing version $($version.NuGetVersion) using API key $($nugetkey)"-Foreground Cyan - -foreach ($project in $projects) -{ - pack($project) -} - - -if ($nopush -eq $false) -{ - foreach ($project in $projects) - { - push($project) - } -} -else -{ - Write-Host "Skipping push" -Foreground Blue -} \ No newline at end of file diff --git a/README.md b/README.md index 4d31212..2b8f6ee 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,14 @@ The documentation for Tapeti is available on Read the Docs: [Master branch](http://tapeti.readthedocs.io/en/stable/)
[![Documentation Status](https://readthedocs.org/projects/tapeti/badge/?version=stable)](http://tapeti.readthedocs.io/en/stable/?badge=stable) + + +## Builds +Builds are automatically run using AppVeyor, with the resulting packages being pushed to NuGet. + + +Latest build +[![Build status](https://ci.appveyor.com/api/projects/status/p5w9yr4xlhvoy02i?svg=true)](https://ci.appveyor.com/project/MvRens/tapeti) + +Master build +[![Build status](https://ci.appveyor.com/api/projects/status/p5w9yr4xlhvoy02i/branch/master?svg=true)](https://ci.appveyor.com/project/MvRens/tapeti/branch/master) diff --git a/Tapeti.Annotations/Tapeti.Annotations.nuspec b/Tapeti.Annotations/Tapeti.Annotations.nuspec index acceaf2..d2684ac 100644 --- a/Tapeti.Annotations/Tapeti.Annotations.nuspec +++ b/Tapeti.Annotations/Tapeti.Annotations.nuspec @@ -1,17 +1,17 @@ - - - - X2Software.Tapeti.Annotations - $version$ - $title$ - Mark van Renswoude - Mark van Renswoude - https://git.x2software.net/pub/tapeti/raw/master/UNLICENSE - https://git.x2software.net/pub/tapeti - https://git.x2software.net/pub/tapeti/raw/master/resources/icons/Tapeti.Annotations.png - false - Annotations for Tapeti - - rabbitmq tapeti - + + + + X2Software.Tapeti.Annotations + $version$ + Tapeti Annotations + Mark van Renswoude + Mark van Renswoude + https://raw.githubusercontent.com/MvRens/Tapeti/master/UNLICENSE + https://github.com/MvRens/Tapeti + https://raw.githubusercontent.com/MvRens/Tapeti/master/resources/icons/Tapeti.Annotations.png + false + Annotations for Tapeti + + rabbitmq tapeti + \ No newline at end of file diff --git a/Tapeti.DataAnnotations/Tapeti.DataAnnotations.nuspec b/Tapeti.DataAnnotations/Tapeti.DataAnnotations.nuspec index a1b9042..571c3a5 100644 --- a/Tapeti.DataAnnotations/Tapeti.DataAnnotations.nuspec +++ b/Tapeti.DataAnnotations/Tapeti.DataAnnotations.nuspec @@ -1,20 +1,20 @@ - - - - X2Software.Tapeti.DataAnnotations - $version$ - $title$ - Mark van Renswoude - Mark van Renswoude - https://git.x2software.net/pub/tapeti/raw/master/UNLICENSE - https://git.x2software.net/pub/tapeti - https://git.x2software.net/pub/tapeti/raw/master/resources/icons/Tapeti.DataAnnotations.png - false - DataAnnotations validation extension for Tapeti - - rabbitmq tapeti dataannotations - - - - + + + + X2Software.Tapeti.DataAnnotations + $version$ + Tapeti DataAnnotations + Mark van Renswoude + Mark van Renswoude + https://raw.githubusercontent.com/MvRens/Tapeti/master/UNLICENSE + https://github.com/MvRens/Tapeti + https://raw.githubusercontent.com/MvRens/Tapeti/master/resources/icons/Tapeti.DataAnnotations.png + false + DataAnnotations validation extension for Tapeti + + rabbitmq tapeti dataannotations + + + + \ No newline at end of file diff --git a/Tapeti.Flow.SQL/Tapeti.Flow.SQL.nuspec b/Tapeti.Flow.SQL/Tapeti.Flow.SQL.nuspec index 686bbf9..0a667c6 100644 --- a/Tapeti.Flow.SQL/Tapeti.Flow.SQL.nuspec +++ b/Tapeti.Flow.SQL/Tapeti.Flow.SQL.nuspec @@ -1,21 +1,21 @@ - - - - X2Software.Tapeti.Flow.SQL - $version$ - $title$ - Mark van Renswoude - Mark van Renswoude - https://git.x2software.net/pub/tapeti/raw/master/UNLICENSE - https://git.x2software.net/pub/tapeti - https://git.x2software.net/pub/tapeti/raw/master/resources/icons/Tapeti.Flow.SQL.png - false - SQL backing repository for the Tapeti Flow package - - rabbitmq tapeti sql - - - - - + + + + X2Software.Tapeti.Flow.SQL + $version$ + Tapeti Flow SQL + Mark van Renswoude + Mark van Renswoude + https://raw.githubusercontent.com/MvRens/Tapeti/master/UNLICENSE + https://github.com/MvRens/Tapeti + https://raw.githubusercontent.com/MvRens/Tapeti/master/resources/icons/Tapeti.Flow.SQL.png + false + SQL backing repository for the Tapeti Flow package + + rabbitmq tapeti sql + + + + + \ No newline at end of file diff --git a/Tapeti.Flow/Tapeti.Flow.nuspec b/Tapeti.Flow/Tapeti.Flow.nuspec index 1df4677..54a7e1f 100644 --- a/Tapeti.Flow/Tapeti.Flow.nuspec +++ b/Tapeti.Flow/Tapeti.Flow.nuspec @@ -1,21 +1,21 @@ - - - - X2Software.Tapeti.Flow - $version$ - $title$ - Menno van Lavieren, Mark van Renswoude - Mark van Renswoude - https://git.x2software.net/pub/tapeti/raw/master/UNLICENSE - https://git.x2software.net/pub/tapeti - https://git.x2software.net/pub/tapeti/raw/master/resources/icons/Tapeti.Flow.png - false - Flow extension for Tapeti - - rabbitmq tapeti flow - - - - - + + + + X2Software.Tapeti.Flow + $version$ + Tapeti Flow + Menno van Lavieren, Mark van Renswoude + Mark van Renswoude + https://raw.githubusercontent.com/MvRens/Tapeti/master/UNLICENSE + https://github.com/MvRens/Tapeti + https://raw.githubusercontent.com/MvRens/Tapeti/master/resources/icons/Tapeti.Flow.png + false + Flow extension for Tapeti + + rabbitmq tapeti flow + + + + + \ No newline at end of file diff --git a/Tapeti.SimpleInjector/Tapeti.SimpleInjector.nuspec b/Tapeti.SimpleInjector/Tapeti.SimpleInjector.nuspec index 6af6063..e53c636 100644 --- a/Tapeti.SimpleInjector/Tapeti.SimpleInjector.nuspec +++ b/Tapeti.SimpleInjector/Tapeti.SimpleInjector.nuspec @@ -1,20 +1,20 @@ - - - - X2Software.Tapeti.SimpleInjector - $version$ - $title$ - Mark van Renswoude - Mark van Renswoude - https://git.x2software.net/pub/tapeti/raw/master/UNLICENSE - https://git.x2software.net/pub/tapeti - https://git.x2software.net/pub/tapeti/raw/master/resources/icons/Tapeti.SimpleInjector.png - false - SimpleInjector integration package for Tapeti - - rabbitmq tapeti simpleinjector - - - - + + + + X2Software.Tapeti.SimpleInjector + $version$ + Tapeti SimpleInjector + Mark van Renswoude + Mark van Renswoude + https://raw.githubusercontent.com/MvRens/Tapeti/master/UNLICENSE + https://github.com/MvRens/Tapeti + https://raw.githubusercontent.com/MvRens/Tapeti/master/resources/icons/Tapeti.SimpleInjector.png + false + SimpleInjector integration package for Tapeti + + rabbitmq tapeti simpleinjector + + + + \ No newline at end of file diff --git a/Tapeti/Tapeti.nuspec b/Tapeti/Tapeti.nuspec index 249c4c2..b1eb7ca 100644 --- a/Tapeti/Tapeti.nuspec +++ b/Tapeti/Tapeti.nuspec @@ -1,20 +1,20 @@ - - - - X2Software.Tapeti - $version$ - $title$ - Mark van Renswoude - Mark van Renswoude - https://git.x2software.net/pub/tapeti/raw/master/UNLICENSE - https://git.x2software.net/pub/tapeti - https://git.x2software.net/pub/tapeti/raw/master/resources/icons/Tapeti.png - false - Controller-based framework for RabbitMQ microservice architectures - - rabbitmq tapeti - - - - + + + + X2Software.Tapeti + $version$ + Tapeti + Mark van Renswoude + Mark van Renswoude + https://raw.githubusercontent.com/MvRens/Tapeti/master/UNLICENSE + https://github.com/MvRens/Tapeti + https://raw.githubusercontent.com/MvRens/Tapeti/master/resources/icons/Tapeti.png + false + Controller-based framework for RabbitMQ microservice architectures + + rabbitmq tapeti + + + + \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..aa68da9 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,40 @@ +image: Visual Studio 2015 + +install: + - choco install gitversion.portable -pre -y + +before_build: + - nuget restore + - ps: gitversion /l console /output buildserver /updateAssemblyInfo + +after_build: + - cmd: ECHO nuget pack Tapeti\Tapeti.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" + - cmd: nuget pack Tapeti\Tapeti.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" + - cmd: appveyor PushArtifact "Tapeti.%GitVersion_NuGetVersion%.nupkg" + - cmd: nuget pack Tapeti.Annotations\Tapeti.Annotations.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" + - cmd: appveyor PushArtifact "Tapeti.Annotations.%GitVersion_NuGetVersion%.nupkg" + - cmd: nuget pack Tapeti.DataAnnotations\Tapeti.DataAnnotations.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" + - cmd: appveyor PushArtifact "Tapeti.DataAnnotations.%GitVersion_NuGetVersion%.nupkg" + - cmd: nuget pack Tapeti.Flow\Tapeti.Flow.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" + - cmd: appveyor PushArtifact "Tapeti.Flow.%GitVersion_NuGetVersion%.nupkg" + - cmd: nuget pack Tapeti.SimpleInjector\Tapeti.SimpleInjector.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" + - cmd: appveyor PushArtifact "Tapeti.SimpleInjector.%GitVersion_NuGetVersion%.nupkg" + +assembly_info: + patch: false + +build: + project: Tapeti.sln + +platform: + - Any CPU + +configuration: + - Release + +deploy: + provider: NuGet + api_key: + secure: wEch4shvYGZUNRW0z/Rm8jdKR95lVnzS0QncQg6a0wH4k/Bt6N/B5bs6AidVcyUD + skip_symbols: false + artifact: /.*\.nupkg/ \ No newline at end of file From 931b242fc0c28105168b70fb7508aeb1034e8dfd Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Tue, 17 Oct 2017 17:27:46 +0200 Subject: [PATCH 17/20] Fixed package names for PushArtifact --- appveyor.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index aa68da9..1984f63 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,15 +10,15 @@ before_build: after_build: - cmd: ECHO nuget pack Tapeti\Tapeti.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" - cmd: nuget pack Tapeti\Tapeti.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" - - cmd: appveyor PushArtifact "Tapeti.%GitVersion_NuGetVersion%.nupkg" + - cmd: appveyor PushArtifact "X2Software.Tapeti.%GitVersion_NuGetVersion%.nupkg" - cmd: nuget pack Tapeti.Annotations\Tapeti.Annotations.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" - - cmd: appveyor PushArtifact "Tapeti.Annotations.%GitVersion_NuGetVersion%.nupkg" + - cmd: appveyor PushArtifact "X2Software.Tapeti.Annotations.%GitVersion_NuGetVersion%.nupkg" - cmd: nuget pack Tapeti.DataAnnotations\Tapeti.DataAnnotations.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" - - cmd: appveyor PushArtifact "Tapeti.DataAnnotations.%GitVersion_NuGetVersion%.nupkg" + - cmd: appveyor PushArtifact "X2Software.Tapeti.DataAnnotations.%GitVersion_NuGetVersion%.nupkg" - cmd: nuget pack Tapeti.Flow\Tapeti.Flow.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" - - cmd: appveyor PushArtifact "Tapeti.Flow.%GitVersion_NuGetVersion%.nupkg" + - cmd: appveyor PushArtifact "X2Software.Tapeti.Flow.%GitVersion_NuGetVersion%.nupkg" - cmd: nuget pack Tapeti.SimpleInjector\Tapeti.SimpleInjector.nuspec -version "%GitVersion_NuGetVersion%" -prop "target=%CONFIGURATION%" - - cmd: appveyor PushArtifact "Tapeti.SimpleInjector.%GitVersion_NuGetVersion%.nupkg" + - cmd: appveyor PushArtifact "X2Software.Tapeti.SimpleInjector.%GitVersion_NuGetVersion%.nupkg" assembly_info: patch: false From b9864df675b3135bfe8fd291cdf6f5b8bdfce12e Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Tue, 17 Oct 2017 17:35:36 +0200 Subject: [PATCH 18/20] Updated NuGet API key --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 1984f63..374feac 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -35,6 +35,6 @@ configuration: deploy: provider: NuGet api_key: - secure: wEch4shvYGZUNRW0z/Rm8jdKR95lVnzS0QncQg6a0wH4k/Bt6N/B5bs6AidVcyUD + secure: pkaN6R8ocu0Q93uCK3DOCifgr1Q4tuH4ZJ4eiV9U5NmwE5qRM2xjUy4B9SkZCsWx skip_symbols: false artifact: /.*\.nupkg/ \ No newline at end of file From 75b14738f470519871e9e6ead7db4d23c6958b9f Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Tue, 17 Oct 2017 17:44:22 +0200 Subject: [PATCH 19/20] Updated build badges --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2b8f6ee..c35e158 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Builds are automatically run using AppVeyor, with the resulting packages being p Latest build -[![Build status](https://ci.appveyor.com/api/projects/status/p5w9yr4xlhvoy02i?svg=true)](https://ci.appveyor.com/project/MvRens/tapeti) +[![Build status](https://ci.appveyor.com/api/projects/status/cyuo0vm7admy0d9x?svg=true)](https://ci.appveyor.com/project/MvRens/tapeti) Master build -[![Build status](https://ci.appveyor.com/api/projects/status/p5w9yr4xlhvoy02i/branch/master?svg=true)](https://ci.appveyor.com/project/MvRens/tapeti/branch/master) +[![Build status](https://ci.appveyor.com/api/projects/status/cyuo0vm7admy0d9x/branch/master?svg=true)](https://ci.appveyor.com/project/MvRens/tapeti/branch/master) From 593a4c4652b433420ea445d825f21ab9d53fefeb Mon Sep 17 00:00:00 2001 From: Menno van Lavieren Date: Fri, 20 Oct 2017 10:12:26 +0200 Subject: [PATCH 20/20] Commit om relase buid te triggeren --- Test/Program.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Test/Program.cs b/Test/Program.cs index 9a71579..a6c110f 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -24,8 +24,6 @@ namespace Test container.Register(); container.Register(); - //container.Register(() => new EF(serviceID)); - var config = new TapetiConfig(new SimpleInjectorDependencyResolver(container)) .WithFlow() .WithDataAnnotations()