A Pen by john Coxhead

Thumbnail
This awesome code was written by jcoxhead127, you can see more from this user in the personal repository.
You can find the original code on Codepen.io
Copyright jcoxhead127 ©
  • HTML
  • JavaScript
<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  <title>A Pen by  john Coxhead</title>
  
  
  
  
  
</head>

<body>

  
  
  

    <script  src="js/index.js"></script>




</body>

</html>

/*Downloaded from https://www.codeseek.co/jcoxhead127/a-pen-by-john-coxhead-dvKzWL */
Some consistence mismatch between the 5 data formats below. I’ve pluralised and removed postfix data. Personally I don’t mind which standard is used so long as it’s consistent. I would suggest having some development standards.

Management Company
{
  "managementCompanyData": [                             "managementCompanies": [
    {
                                                              "managementCompanyCode": "string",
      "taPlatformManagementCompanyCode": "string", 
      "taPlatformSourceCode": "string",
      "name": "string",                                        "legalName": "string",
      "countryOfDomicile": "string"
   }
  ]
}

Umbrella Fund
{
  "umbrellaFund": [                                     "umbrellaFunds": [
    {
                                                              "umbrellaFundCode": "string",
      "managementCompanyCode": "string",
      "platformUmbrellaCode": "string",                         "platformCode": "string",
      "platformSystemSourceCode": "string",                     "platformSource": "string",
      "name": "string",                                        "legalName": "string",
      "countryOfDomicile": "string",
      "typeOfVehicle": "string"
    }
  ]
}

Sub Fund
{
  "fundData": [                                         "subFunds": [
    {
      "subFundCode": "string",
      "umbrellaFundCode": "string",
      "platformUmbrellaCode": "string",                         "platformCode": "string",
      "platformSystemSourceCode": "string",                     "platformSource": "string",
      "name": "string",                                        "legalName": "string",
      "currency": "string",
      "launchDate": "2017-03-22T10:19:35.716Z",
      "closureDate": "2017-03-22T10:19:35.716Z",
      "investmentDesk": "string"
    }
  ]
}

Share Class
{
  "shareClasses": [
    {
      "code": "string",                                        "shareClassCode": "string",
                                                             "subFundCode": "string",                   New
      "platformCode": "string",
      "platformSource": "string",
      "currency": "string",
      "legalName": "string",
      "launchDate": "2017-03-22T10:21:20.454Z",
      "closureDate": "2017-03-22T10:21:20.454Z",
      "shareClassIncomeType": "string",                         "IncomeType": "string",
      "shareClassIdentifierType": "string",                     "shareClassIdentifierType": "string",
      "bloomberg": "string",
      "valoren": 0,
      "sedolNumber": "string",
      "cedel": 0,
      "isinNumber": "string",                                    "isin": "string",                         or ISINCode, not Number
      "hedging": "string"                                                                               This data will probably be wrong, be careful…
    }
  ]
}


Share Class
{
  "shareClasseRates": [
    {
      "code": "string",                                        "shareClasseRateCode": "string",
      "shareClassCode": "string",
      "systemSource": "string",
      "entryCharge": 0,
      "exitCharge": 0,
      "switchCharge": 0,
      "ogcKiidFee": 0,
      "managementFee": 0,
      "distributionFee": 0,
      "safekeepingFee": 0,
      "taFee": 0,
      "statutoryFee": 0,
      "transactionFee": 0,
      "fundAccountingFee": 0,
      "trusteeFee": 0,
      "prospectusManagementFee": 0,
      "prospectusCustodyFee": 0,
      "luxTax": 0,
      "fiduciaryFee": 0,
      "securitiesLendingFee": 0,
      "custodyFee": 0
    }
  ]
}

{
  "version": "1.0.0-*",

  "dependencies": {
    "Autofac": "4.2.0",
    "Microsoft.Extensions.Logging": "1.1.0",
    "Moq": "4.5.28",
    "MSTest.TestFramework": "1.0.6-preview"
  },

  "frameworks": {
    "net461": {
    }
  }
}

using Moq;
using Microsoft.Extensions.Logging;

namespace Schroders.Test.Infrastructure.Common
{
    public static class CommonMocks
    {
        public static Mock<ILoggerFactory> GetLoggerFactory()
        {
            var loggerMock = new Mock<ILogger>();
            var loggerFactoryMock = new Mock<ILoggerFactory>();
            loggerFactoryMock.Setup(x => x.CreateLogger(It.IsAny<string>()))
                .Returns(loggerMock.Object);

            return loggerFactoryMock;
        }
    }
}


{
  "version": "1.0.0-*",

  "dependencies": {
    "Microsoft.Extensions.DependencyInjection.Abstractions": "1.1.0",
    "Newtonsoft.Json": "9.0.1",
    "System.Net.Http": "4.0.0"
  },

  "frameworks": {
    "net461": {
    }
  }
}
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Schroders.ServiceBase.RestClient
{
    public class RestRequest
    {
        private readonly List<string> urlSegments;
        private readonly List<string> urlParameters;

        public string Url => BuildUrl();
        public string Token { get; private set; }

        public IDictionary<string, string> Headers { get; set; }

        public RestRequest()
        {
            urlSegments = new List<string>();
            urlParameters = new List<string>();
            Headers = new Dictionary<string, string>();
        }

        public RestRequest AddUrlParameter(string name, string value)
        {
            urlParameters.Add($"{name}={value}");
            return this;
        }

        public RestRequest AddUrlSegment(string urlSegment)
        {
            urlSegments.Add(urlSegment);
            return this;
        }

        public RestRequest AddHeaders(IDictionary<string, string> headers)
        {
            Headers = Headers.Concat(headers).ToDictionary(x => x.Key, x => x.Value);
            return this;
        }

        public RestRequest AddAuthorizationHeader(string authorizationHeader)
        {
            Headers.Add("Authorization", authorizationHeader);
            return this;
        }

        public RestRequest AddToken(string token)
        {
            this.Token = token;
            Headers.Add("Authorization", $"Bearer {token}");
            return this;
        }

        private string BuildUrl()
        {
            var url = new StringBuilder();

            urlSegments.ForEach(x => url.Append($"/{x}"));

            if (urlParameters.Any())
            {
                url.Append($"?{urlParameters.FirstOrDefault()}");
                
                urlParameters.Skip(1).ToList().ForEach(x => url.Append($"&{x}"));
            }

            return url.ToString();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace Schroders.ServiceBase.RestClient
{
    public class RestClient: IRestClient
    {
        public string BaseUrl { get; set; }
        private readonly IHttpClient httpClient;

        public RestClient(IHttpClient httpClient = null)
        {
            this.httpClient = httpClient ?? new HttpClient();
        }

        public async Task<HttpResponse<TResponse>> Get<TResponse>(RestRequest request)
        {
            var response = await MakeHttpRequest<TResponse>(HttpMethod.Get.Method, request);
            return response;
        }

        public async Task<HttpResponse<TResponse>> Post<TRequest, TResponse>(RestRequest request, TRequest requestData)
        {
            var response = await MakeHttpRequest<TRequest, TResponse>(HttpMethod.Post.Method, request, requestData);
            return response;
        }

        private async Task<HttpResponse<TResponse>> MakeHttpRequest<TResponse>(string method, RestRequest request)
        {
            return await MakeHttpRequest<object, TResponse>(method, request, null);
        }

        private async Task<HttpResponse<TResponse>> MakeHttpRequest<TRequest, TResponse>(string method, RestRequest request, TRequest requestData)
        {
            var requestMessage = CreateHttpRequestMessage(method, request, requestData);
            
            var responseMessage = await httpClient.SendAsync(requestMessage);
            responseMessage.EnsureSuccessStatusCode();

            var responseContent = ParseResponse<TResponse>(responseMessage);

            return new HttpResponse<TResponse>
            {
                Content = responseContent.Result,
                StatusCode = responseMessage.StatusCode
            };
        }

        private HttpRequestMessage CreateHttpRequestMessage<TRequest>(string method, RestRequest request, TRequest requestData)
        {
            var url = BuildUrl(BaseUrl, request.Url);

            var requestMessage = new HttpRequestMessage
            {
                Method = new HttpMethod(method),
                RequestUri = new Uri(url)
            };

            if (requestData != null)
            {
                var jsonString = JsonConvert.SerializeObject(requestData);
                requestMessage.Content = new StringContent(jsonString, Encoding.UTF8, "application/json");
            }

            AddHeaders(requestMessage, request.Headers);

            return requestMessage;
        }

        private static async Task<TResponse> ParseResponse<TResponse>(HttpResponseMessage responseMessage)
        {
            var responseMessageContent = await responseMessage.Content.ReadAsStringAsync();
            var responseContent = JsonConvert.DeserializeObject<TResponse>(responseMessageContent);
            return responseContent;
        }

        private static void AddHeaders(HttpRequestMessage requestMessage, IDictionary<string, string> headers)
        {
            foreach (var header in headers)
            {
                if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value))
                {
                    requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value);
                }
            }
        }

        private static string BuildUrl(string baseUrl, string requestUrl)
        {
            if (string.IsNullOrEmpty(baseUrl))
            {
                throw new ArgumentNullException(nameof(baseUrl), "Service base URL is not set in rest client.");
            }

            var url = baseUrl;
            if (url.Last() == '/')
            {
                url.Remove(url.Length-1);
            }

            url += requestUrl;
            return url;
        }
    }
}


using System.Threading.Tasks;

namespace Schroders.ServiceBase.RestClient
{
    public interface IRestClient
    {
        string BaseUrl { get; set; }
        Task<HttpResponse<TResponse>> Get<TResponse>(RestRequest request);
        Task<HttpResponse<TResponse>> Post<TRequest, TResponse>(RestRequest request, TRequest requestData);       
    }
}


using System.Net.Http;
using System.Threading.Tasks;

namespace Schroders.ServiceBase.RestClient
{
    public interface IHttpClient
    {
        Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
    }
}

using System.Net;

namespace Schroders.ServiceBase.RestClient
{
    public class HttpResponse<TResponse>
    {
        public TResponse Content { get; set; }
        public HttpStatusCode StatusCode { get; set; }
    }
}
namespace Schroders.ServiceBase.RestClient
{
    public class HttpClient: System.Net.Http.HttpClient, IHttpClient
    {
    }
}


using Microsoft.Extensions.DependencyInjection;

namespace Schroders.ServiceBase.RestClient.Extensions
{
    public static class RestClientServiceCollectionExtensions
    {
        public static void AddRestClient(this IServiceCollection services)
        {
            services.AddSingleton<IRestClient, RestClient>();
        }
    }
}
{
  "version": "1.0.0-*",

  "dependencies": {
    "Schroders.ServiceBase.RestClient": "1.0.0-*",
    "Schroders.ServiceDiscovery.RestClient": "1.0.1",
    "System.Net.Http": "4.0.0"
  },

  "frameworks": {
    "net461": {}
  }
}

namespace Schroders.ServiceBase.ResolvableServiceClient
{
    public class GetServiceUrlResponse
    {
        public string ServiceUrl { get; set; }
    }
}

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Schroders.ServiceDiscovery.RestClient.Extensions;

namespace Schroders.ServiceBase.ResolvableServiceClient.Extensions
{
    public static class ResolvableServiceClientServiceCollectionExtensions
    {
        public static void AddResolvableServiceClient(this IServiceCollection services, IConfigurationRoot configuration)
        {
            services.AddServiceDiscoveryRestClient(configuration);
        }
    }
}
{
  "version": "1.0.0-*",

  "dependencies": {
    "Microsoft.AspNetCore.Hosting": "1.1.0",
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
    "Microsoft.AspNetCore.Rewrite": "1.0.0",
    "Topshelf": "4.0.3",
    "Microsoft.AspNetCore.Server.Kestrel.Https": "1.1.0"
  },

  "frameworks": {
    "net461": {}
  }
}
{
  "version": "1.0.0-*",

  "dependencies": {
    "Microsoft.AspNetCore.Hosting": "1.1.0",
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
    "Microsoft.AspNetCore.Rewrite": "1.0.0",
    "Topshelf": "4.0.3",
    "Microsoft.AspNetCore.Server.Kestrel.Https": "1.1.0"
  },

  "frameworks": {
    "net461": {}
  }
}


using System;
using Topshelf;

namespace Schroders.ServiceBase.Hosting
{
    public static class HostingFacade
    {
        public static void Run<TService>(Func<TService> serviceCreator) where TService : HostableService
        {
            HostFactory.Run(cfg =>
            {
                var service = serviceCreator();
                cfg.Service(() => service);

                cfg.SetServiceName(service.ApplicationName);

                service.UpdateHostConfigurator(cfg);
            });
        }
    }
}

namespace Schroders.ServiceBase.Hosting
{
    public class HostingConfiguration
    {
        public string ApplicationName { get; set; }

        public string BaseUrl { get; set; }

        public int HttpPort { get; set; }

        public int HttpsPort { get; set; }

        public bool IsSslEnabled { get; set; }

        public string CertificateFile { get; set; }

        public string CertificatePassword { get; set; }

        public string CertificateThumbprint { get; set; }

        public bool ForceHttpsRedirect { get; set; }
    }
}

using Microsoft.AspNetCore.Hosting;
using Topshelf;
using Topshelf.HostConfigurators;

namespace Schroders.ServiceBase.Hosting
{
    public abstract class HostableService : ServiceControl
    {
        private IWebHost host;

        private readonly HostingConfiguration config;

        private readonly string contentRoot;

        public string ApplicationName => config.ApplicationName;

        protected HostableService(HostingConfiguration config, string contentRoot)
        {
            this.config = config;
            this.contentRoot = contentRoot;
        }

        protected abstract IWebHost CreateHost(HostingConfiguration config, string contentRoot);

        public abstract void UpdateHostConfigurator(HostConfigurator cfg);

        public bool Start(HostControl hostControl)
        {
            this.host = CreateHost(this.config, this.contentRoot);

            this.host.Start();

            return true;
        }

        public bool Stop(HostControl hostControl)
        {
            this.host?.Dispose();

            return true;
        }
    }
}
using System;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;

namespace Schroders.ServiceBase.Hosting.Helpers
{
    public static class CertificateHelper
    {
        public static X509Certificate2 GetCertificate(string filePath, string filePassword, string thumbprint)
        {
            if (!string.IsNullOrEmpty(filePath) && !string.IsNullOrEmpty(filePassword))
            {
                return new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + filePath, filePassword);
            }

            if (!string.IsNullOrEmpty(thumbprint))
            {
                thumbprint = Regex.Replace(thumbprint, @"[^\da-zA-z]", string.Empty).ToUpper();

                var certCollection = OpenCertificateCollection(thumbprint, StoreName.My, StoreLocation.CurrentUser);
                if (certCollection.Count == 0)
                {
                    certCollection = OpenCertificateCollection(thumbprint, StoreName.My, StoreLocation.LocalMachine);
                }

                if (certCollection.Count > 0)
                {
                    return certCollection[0];
                }
            }

            return null;
        }

        private static X509Certificate2Collection OpenCertificateCollection(string thumbprint, StoreName name, StoreLocation location)
        {
            var store = new X509Store(name, location);
            store.Open(OpenFlags.ReadOnly);
            var certCollection = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
            store.Close();
            return certCollection;
        }
    }
}


using System;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.Extensions.Configuration;

namespace Schroders.ServiceBase.Hosting.Extensions
{
    public static class UrlRewriterExtensions
    {
        public static IApplicationBuilder UseRewriterToForceHttpsRedirect(this IApplicationBuilder app, IConfigurationRoot configuration, RewriteOptions rewriteOptions = null)
        {
            rewriteOptions = rewriteOptions ?? new RewriteOptions();

            var section = configuration.GetSection(nameof(HostingConfiguration));
            var config = new HostingConfiguration();
            section.Bind(config);

            if (config.ForceHttpsRedirect)
            {
                rewriteOptions.Rules.Insert(0, new RedirectToHttpsRule
                {
                    SSLPort = config.HttpsPort,
                    StatusCode = 301
                });
            }

            if (rewriteOptions.Rules.Any())
            {
                app.UseRewriter(rewriteOptions);
            }

            return app;
        }
    }
}

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel;
using Schroders.ServiceBase.Hosting.Helpers;

namespace Schroders.ServiceBase.Hosting.Extensions
{
    public static class KestrelServerOptionsExensions
    {
        public static void AddSharedKestrelOptions(this KestrelServerOptions options, HostingConfiguration config)
        {
            options.AddServerHeader = false;

            if (!config.IsSslEnabled)
            {
                return;
            }

            var certificate = CertificateHelper.GetCertificate(config.CertificateFile, config.CertificatePassword, config.CertificateThumbprint);

            options.UseHttps(certificate);

        }
    }
}

namespace Schroders.ServiceBase.Hosting.Extensions
{
    public static class HostingConfigurationExtensions
    {
        public static string GetUrls(this HostingConfiguration config)
        {
            var urls = GetUrl("http", config.BaseUrl, config.HttpPort.ToString());

            if (config.IsSslEnabled)
            {
                urls += ";";
                urls += GetUrl("https", config.BaseUrl, config.HttpsPort.ToString());
            }

            return urls;
        }

        private static string GetUrl(string protocol, string baseUrl, string port)
        {
            return $"{protocol}://{baseUrl}:{port}";
        }
    }
}


{
  "version": "1.0.0-*",

  "dependencies": {
    "Schroders.Logging.Core": "1.0.0-*",
    "Schroders.ServiceBase.Commands": "1.0.0-*"
  },

  "frameworks": {
    "net461": {}
  }
}

using System;
using System.Collections.Generic;
using Schroders.Logging.Core.Constants;
using Schroders.ServiceBase.Commands.Pipeline.PipelineAction;
using Schroders.ServiceBase.Commands.PipelineActions;
using Serilog.Context;
using Serilog.Core;
using Serilog.Core.Enrichers;

namespace Schroders.ServiceBase.Commands.Serilog.PipelineActions
{
    public class LoggingPipelineAction<TRequest, TContext> : IPipelineAction<TRequest, TContext>
        where TContext : IPipelineActionContext
    {
        public void Execute(TContext context, Action<TContext> next)
        {
            var properties = GetProperties(context).ToArray();

            using (LogContext.PushProperties(properties))
            {
                next(context);
            }
        }

        private static List<ILogEventEnricher> GetProperties(TContext context)
        {
            var properties = new List<ILogEventEnricher>();

            var productName = context[LoggingContextConstants.ProductName];
            if (productName != null)
            {
                properties.Add(new PropertyEnricher(LoggingConstants.ProductName, productName));
            }

            var traceLegId = context[LoggingContextConstants.TraceLegId];
            if (traceLegId != null)
            {
                properties.Add(new PropertyEnricher(LoggingConstants.TraceLegId, traceLegId));
            }

            var traceId = context[GenerateIdPipelineActionBase.GenerateIdPipelineActionContextId];
            if (traceId != null)
            {
                properties.Add(new PropertyEnricher(LoggingConstants.TraceId, traceId));
            }

            return properties;
        }
    }
}

{
  "version": "1.0.0-*",

  "dependencies": {
    "Autofac": "4.2.0",
    "Microsoft.Extensions.Logging.Abstractions": "1.1.0",
    "Newtonsoft.Json": "9.0.1",
    "Serilog": "2.3.0",
    "System.Runtime.Serialization.Primitives": "4.3.0",
    "Polly-Signed": "4.3.0"
  },

  "frameworks": {
    "net461": {}
  }
}
using System.Collections.Generic;

namespace Schroders.ServiceBase.Commands
{
    public interface ICommand<in TRequest, out TResponse>
    {
        TResponse Execute(TRequest request, IDictionary<string, object> context);
    }
}

using System;
using System.Diagnostics;

namespace Schroders.ServiceBase.Commands
{
    public class DisposableStopwatch : IDisposable
    {
        private readonly Stopwatch sw;
        private readonly Action<TimeSpan> f;

        public DisposableStopwatch(Action<TimeSpan> f)
        {
            this.f = f;
            sw = Stopwatch.StartNew();
        }

        public void Dispose()
        {
            sw.Stop();
            f(sw.Elapsed);
        }
    }
}

using System;
using Microsoft.Extensions.Logging;
using Schroders.ServiceBase.Commands.Pipeline.PipelineAction;

namespace Schroders.ServiceBase.Commands.PipelineActions
{
    public class TracePipelineAction<TRequest, TContext> : IPipelineAction<TRequest, TContext>
    {
        private readonly ILogger<TracePipelineAction<TRequest, TContext>> logger;

        public TracePipelineAction(ILogger<TracePipelineAction<TRequest, TContext>> logger)
        {
            this.logger = logger;
        }

        public void Execute(TContext context, Action<TContext> next)
        {
            using (new DisposableStopwatch(ts => this.logger.LogInformation("{@ts} elapsed decorated", ts)))
            {
                next(context);
            }
        }
    }
}

using System;
using Microsoft.Extensions.Logging;
using Polly;
using Schroders.ServiceBase.Commands.Pipeline.PipelineAction;

namespace Schroders.ServiceBase.Commands.PipelineActions
{
    public class RetryPipelineAction<TRequest, TContext> : IPipelineAction<TRequest, TContext>
        where TContext : IPipelineActionContext
    {
        private readonly ILogger<RetryPipelineAction<TRequest, TContext>> logger;

        public RetryPipelineAction(ILogger<RetryPipelineAction<TRequest, TContext>> logger)
        {
            this.logger = logger;
        }

        public void Execute(TContext context, Action<TContext> next)
        {
            var policy = Policy.Handle<Exception>().Retry(
                2,
                (exception, retryCount) =>
                    {
                        this.logger.LogWarning("Retry error handler cought: {@exception}, retry count: {@retryCount}, operation id: {@context.GetId()}", exception, retryCount, context.GetId());
                    });

            var policyResult = policy.ExecuteAndCapture(() => next(context));
            if (policyResult.Outcome != OutcomeType.Failure)
            {
                return;
            }

            this.logger.LogError("Retry pipeline action failed with exception: {@policyResult.FinalException}", policyResult.FinalException);
            throw policyResult.FinalException;
        }
    }
}

using Schroders.ServiceBase.Commands.Pipeline.PipelineAction;

namespace Schroders.ServiceBase.Commands.PipelineActions
{
    public static class GenerateIdPipelineActionContextExtensions
    {
        public static string GetId(this IPipelineActionContext pipelineActionContext)
        {            
            if (pipelineActionContext == null || !pipelineActionContext.ContainsKey(GenerateIdPipelineActionBase.GenerateIdPipelineActionContextId))
            {
                return null;
            }

            return pipelineActionContext[GenerateIdPipelineActionBase.GenerateIdPipelineActionContextId].ToString();
        }
    }
}


namespace Schroders.ServiceBase.Commands.PipelineActions
{
    public abstract class GenerateIdPipelineActionBase
    {
        public const string GenerateIdPipelineActionContextId = "ContextId";
    }
}

using System;
using Microsoft.Extensions.Logging;
using Schroders.ServiceBase.Commands.Pipeline.PipelineAction;

namespace Schroders.ServiceBase.Commands.PipelineActions
{
    public class GenerateIdPipelineAction<TRequest, TContext> : GenerateIdPipelineActionBase, IPipelineAction<TRequest, TContext>
        where TContext : IPipelineActionContext
    {
        private readonly ILogger<GenerateIdPipelineAction<TRequest, TContext>> logger;

        public GenerateIdPipelineAction(ILogger<GenerateIdPipelineAction<TRequest, TContext>> logger)
        {
            this.logger = logger;
        }

        public void Execute(TContext context, Action<TContext> next)
        {
            if (context != null && !context.ContainsKey(GenerateIdPipelineActionBase.GenerateIdPipelineActionContextId))
            {
                context[GenerateIdPipelineActionBase.GenerateIdPipelineActionContextId] = Guid.NewGuid();
            }

            this.logger.LogInformation("Pipeline call context: {@context}", context);
            next(context);
        }
    }
}

using System;
using Microsoft.Extensions.Logging;
using Schroders.ServiceBase.Commands.Pipeline.PipelineAction;

namespace Schroders.ServiceBase.Commands.PipelineActions
{
    public class ErrorHandlingPipelineAction<TRequest, TContext> : IPipelineAction<TRequest, TContext>
        where TContext : IPipelineActionContext
    {
        private readonly ILogger<ErrorHandlingPipelineAction<TRequest, TContext>> logger;

        public ErrorHandlingPipelineAction(ILogger<ErrorHandlingPipelineAction<TRequest, TContext>> logger)
        {
            this.logger = logger;
        }

        public void Execute(TContext context, Action<TContext> next)
        {
            try
            {
                next(context);
            }
            catch (Exception e)
            {
                if (context != null)
                {
                    context.Exception = e;
                }

                this.logger.LogError("Unhandled exception cought: {@e}, context: {@context}", e, context);
            }
        }
    }
}
namespace Schroders.ServiceBase.Commands.Pipeline
{
    public class PipelineResult<TResponse, TContext> : IPipelineResult<TResponse, TContext>
    {
        private PipelineResult()
        {
        }

        public bool Success { get; private set; }

        public TResponse Result { get; private set; }

        public TContext Context { get; private set; }

        public ErrorResult Error { get; private set; }

        public static PipelineResult<TResponse, TContext> CreateSuccess(TResponse response, TContext context)
        {
            return new PipelineResult<TResponse, TContext> { Success = true, Result = response, Context = context };
        }

        public static PipelineResult<TResponse, TContext> CreateFailure(ErrorResult errorResult)
        {
            return new PipelineResult<TResponse, TContext> { Success = false, Error = errorResult };
        }

        public static PipelineResult<TResponse, TContext> CreateFailure(ErrorResult errorResult, TResponse response, TContext context)
        {
            return new PipelineResult<TResponse, TContext> { Success = false, Result = response, Context = context, Error = errorResult };
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using Schroders.ServiceBase.Commands.Pipeline.PipelineAction;

namespace Schroders.ServiceBase.Commands.Pipeline
{
    public class Pipeline<TRequest, TResponse, TContext> : IPipeline<TRequest, TResponse, TContext>
        where TContext : IPipelineActionContext, new()
    {
        private readonly IPipelineAction<TRequest, TContext>[] prePostActions;
        private readonly ICommand<TRequest, TResponse> commandAction;
        private TRequest request;

        public Pipeline(IPipelineAction<TRequest, TContext>[] prePostActions, ICommand<TRequest, TResponse> commandAction)
        {
            this.prePostActions = prePostActions;
            this.commandAction = commandAction;
        }

        public virtual IPipelineResult<TResponse, TContext> Execute(TRequest requestParam, IDictionary<string, object> initialContext = null)
        {
            var context = this.InitializeContext(initialContext);

            this.request = requestParam;
            
            Action<TContext> wrappedAction = this.DefaultExecuteAction;

            foreach (var prePostAction in this.prePostActions.Reverse())
            {
                var copy = wrappedAction;
                wrappedAction = c =>
                {
                    prePostAction.Execute(c, copy);
                };
            }

            // Todo: handle error of a pipeline
            wrappedAction(context);

            if (context.Exception != null)
            {
                return PipelineResult<TResponse, TContext>.CreateFailure(ErrorResult.FromException(context.Exception));
            }

            var result = GetResultOrDefault<TResponse>(context.Result);
            return PipelineResult<TResponse, TContext>.CreateSuccess(result, context);
        }

        protected virtual TContext InitializeContext(IDictionary<string, object> initialContext)
        {
            var context = new TContext();
            var initialContextValue = initialContext ?? new Dictionary<string, object>();
            foreach (var keyValue in initialContextValue)
            {
                context.Add(keyValue);
            }

            return context;
        }

        private void DefaultExecuteAction(TContext contextParam)
        {
            var result = default(TResponse);

            try
            {
                if (!contextParam.Abort)
                {
                    result = this.commandAction.Execute(this.request, contextParam);
                }
            }
            catch (Exception e)
            {
                contextParam.Exception = e;
                throw;
            }

            contextParam.Result = result;
        }

        private static T GetResultOrDefault<T>(object input)
        {
            T result;

            try
            {
                result = (T)input;
            }
            catch (Exception)
            {
                result = default(T);
            }

            return result;
        }
    }
}
namespace Schroders.ServiceBase.Commands.Pipeline
{
    public interface IPipelineResult<TResponse, TContext>
    {
        bool Success { get; }

        TResponse Result { get; }

        TContext Context { get; }

        ErrorResult Error { get; }
    }
}

using System.Collections.Generic;
using Schroders.ServiceBase.Commands.Pipeline.PipelineAction;

namespace Schroders.ServiceBase.Commands.Pipeline
{
    public interface IPipeline<in TRequest, TResponse, TContext>
        where TContext : IPipelineActionContext, new()
    {
        IPipelineResult<TResponse, TContext> Execute(TRequest requestParam, IDictionary<string, object> initialContext = null);
    }
}

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Schroders.ServiceBase.Commands.Pipeline
{
    public class ErrorResult : ReadOnlyCollection<Error>
    {
        public ErrorResult(IList<Error> list)
            : base(list)
        {
        }

        public static ErrorResult FromException(Exception exception)
        {
            return new ErrorResult(new[]
                                       {
                                           new Error("Exception", exception.Message)
                                       });
        }
    }
}
using System;
using Newtonsoft.Json;

namespace Schroders.ServiceBase.Commands.Pipeline
{
    public class Error
    {
        public Error(string errorCode, string messageShort, string messageLong = null, Uri errorUrl = null)
        {
            this.ErrorCode = errorCode;
            this.MessageShort = messageShort;
            this.MessageLong = messageLong;
            this.ErrorUrl = errorUrl;
        }

        [JsonProperty(PropertyName = "errorCode")]
        public string ErrorCode { get; private set; }

        [JsonProperty(PropertyName = "messageShort")]
        public string MessageShort { get; private set; }

        [JsonProperty(PropertyName = "messageLong")]
        public string MessageLong { get; private set; }

        [JsonProperty(PropertyName = "errorUrl")]
        public Uri ErrorUrl { get; private set; }
    }
}

using Autofac;
using Schroders.ServiceBase.Commands.Pipeline.PipelineAction;

namespace Schroders.ServiceBase.Commands.Pipeline.PipelineFactory
{
    public class PipelineFactory : IPipelineFactory
    {
        private readonly IComponentContext container;

        public PipelineFactory(IComponentContext container)
        {
            this.container = container;
        }

        public IPipeline<TRequest, TResponse, PipelineActionContext> Get<TRequest, TResponse>()
        {
            var pipeline = this.container.Resolve<IPipeline<TRequest, TResponse, PipelineActionContext>>();

            return pipeline;
        }
    }
}

using Schroders.ServiceBase.Commands.Pipeline.PipelineAction;

namespace Schroders.ServiceBase.Commands.Pipeline.PipelineFactory
{
    public interface IPipelineFactory
    {
        IPipeline<TRequest, TResponse, PipelineActionContext> Get<TRequest, TResponse>();
    }
}
using System;

namespace Schroders.ServiceBase.Commands.Pipeline.PipelineAction
{
    public interface IPipelineAction<TRequest, TContext>
    {
        void Execute(TContext context, Action<TContext> next);
    }
}
using System;
using System.Collections.Generic;

namespace Schroders.ServiceBase.Commands.Pipeline.PipelineAction
{
    public interface IPipelineActionContext : IDictionary<string, object>
    {
        bool Abort { get; set; }

        Exception Exception { get; set; }

        object Result { get; set; }
    }
}

using System;
using System.Collections.Generic;

namespace Schroders.ServiceBase.Commands.Pipeline.PipelineAction
{
    public class PipelineActionContext : Dictionary<string, object>, IPipelineActionContext
    {
        public bool Abort { get; set; }

        public Exception Exception { get; set; }

        public object Result { get; set; }
    }
}

{
  "version": "1.0.0-*",

  "dependencies": {
    "Microsoft.AspNetCore.Http.Abstractions": "1.1.0",
    "Schroders.Logging.Core": "1.0.0-*",
    "Serilog": "2.3.0"
  },

  "frameworks": {
    "net461": {}
  }
}
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Schroders.Logging.Core.Constants;
using Schroders.Logging.Core.Extensions;
using Serilog.Context;
using Serilog.Core;
using Serilog.Core.Enrichers;

namespace Schroders.Logging.Serilog.Middleware
{
    public class SchrodersLoggingEnrichMiddleware
    {
        private readonly RequestDelegate next;
        public SchrodersLoggingEnrichMiddleware(RequestDelegate next)
        {
            this.next = next;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            var properties = GetProperties(httpContext).ToArray();

            using (LogContext.PushProperties(properties))
            {
                await next.Invoke(httpContext);
            }
        }

        private static List<ILogEventEnricher> GetProperties(HttpContext httpContext)
        {
            var properties = new List<ILogEventEnricher>();

            var productName = httpContext.Request.Headers[LoggingHeaderConstants.ProductName].FirstOrDefault();
            if (productName != null)
            {
                properties.Add(new PropertyEnricher(LoggingConstants.ProductName, productName));
            }

            if (httpContext.TraceIdentifier != null)
            {
                properties.Add(new PropertyEnricher(LoggingConstants.TraceId, httpContext.TraceIdentifier));
            }

            var traceLegId = httpContext.GetLoggingContext().TraceLegId;
            if (traceLegId != null)
            {
                properties.Add(new PropertyEnricher(LoggingConstants.TraceLegId, traceLegId));
            }

            return properties;
        }
    }
}
using System.Diagnostics;
using System.Reflection;
using Schroders.Logging.Core.Constants;
using Serilog;
using Serilog.Configuration;

namespace Schroders.Logging.Serilog.Extensions
{
    public static class LoggerEnrichmentConfigurationExtentions
    {
        public static LoggerConfiguration WithSchrodersSettings(this LoggerEnrichmentConfiguration enrichmentConfiguration, string applicationName)
        {
            var loggerConfiguration = enrichmentConfiguration.WithProperty(LoggingConstants.ApplicationName, applicationName);

            var version = Assembly.GetExecutingAssembly().GetName().Version;
            loggerConfiguration.Enrich.WithProperty(LoggingConstants.ApplicationVersion, version);

            var processId = Process.GetCurrentProcess().Id;
            loggerConfiguration.Enrich.WithProperty(LoggingConstants.ApplicationInstanceId, processId);

            return loggerConfiguration;
        }
    }
}
{
  "version": "1.0.0-*",

  "dependencies": {
    "Microsoft.AspNetCore.Http.Abstractions":  "1.1.0"
  },

  "frameworks": {
    "net461": {}
  }
}

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Schroders.Logging.Core.Constants;
using Schroders.Logging.Core.Extensions;

namespace Schroders.Logging.Core.Middleware
{
    public class SchrodersLoggingMiddleware
    {
        private readonly RequestDelegate next;
        public SchrodersLoggingMiddleware(RequestDelegate next)
        {
            this.next = next;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            var traceLegId = httpContext.Request.Headers[LoggingHeaderConstants.TraceLegId].FirstOrDefault() ?? Guid.NewGuid().ToString();
            httpContext.GetLoggingContext().TraceLegId = traceLegId;

            await next.Invoke(httpContext);
        }
    }
}

using System.Linq;
using Schroders.Logging.Core.Constants;

namespace Schroders.Logging.Core.Extensions
{
    public static class HttpContextExtractorExtensions
    {
        public static HttpContextExtractor ExtractLoggingContext(this HttpContextExtractor extractor)
        {
            var productName = extractor.RequestContext.Request.Headers[LoggingHeaderConstants.ProductName].FirstOrDefault();
            extractor.Add(LoggingContextConstants.ProductName, productName);
            extractor.Add(LoggingContextConstants.TraceLegId, extractor.RequestContext.GetLoggingContext().TraceLegId);

            return extractor;
        }
    }
}

using System.Collections.Generic;
using Microsoft.AspNetCore.Http;

namespace Schroders.Logging.Core.Extensions
{
    public class HttpContextExtractor : Dictionary<string, object>
    {
        public HttpContext RequestContext { get; }

        public HttpContextExtractor(HttpContext context)
        {
            this.RequestContext = context;
        }
    }
}

using Microsoft.AspNetCore.Http;
using Schroders.Logging.Core.Context;

namespace Schroders.Logging.Core.Extensions
{
    public static class HttpContextExtensions
    {
        public static LoggingContext GetLoggingContext(this HttpContext httpContext)
        {
            const string loggingContextItemName = "LoggingContext";

            if (httpContext.Items[loggingContextItemName] == null)
            {
                httpContext.Items[loggingContextItemName] = new LoggingContext();
            }

            return httpContext.Items[loggingContextItemName] as LoggingContext;
        }
    }
}


namespace Schroders.Logging.Core.Context
{
    public class LoggingContext
    {
        public string TraceLegId { get; set; }
    }
}

namespace Schroders.Logging.Core.Constants
{
    public class LoggingHeaderConstants
    {
        public const string TraceLegId = "TraceLegId";
        public const string ProductName = "ProductName";
    }
}

namespace Schroders.Logging.Core.Constants
{
    public class LoggingContextConstants
    {
        public const string ProductName = "ProductName";
        public const string TraceLegId = "TraceLegId";
    }
}

namespace Schroders.Logging.Core.Constants
{
    public class LoggingConstants
    {
        public const string ProductName = "ProductName";
        public const string ApplicationName = "ApplicationName";
        public const string ApplicationVersion = "ApplicationVersion";
        public const string ApplicationInstanceId = "ApplicationInstanceId";
        public const string TraceId = "TraceId";
        public const string TraceLegId = "TraceLegId";
    }
}
{
  "version": "1.0.0-*",

  "dependencies": {
    "Newtonsoft.Json": "9.0.1",
    "Schroders.Storage.Core": "1.0.0-*"
  },

  "frameworks": {
    "net461": {}
  }
}
namespace Schroders.Storage.MemoryDocumentStorage
{
    using Newtonsoft.Json;

    public static class ObjectCopier
    {
        /// <summary>
        /// Perform a deep Copy of the object, using Json as a serialisation method. NOTE: Private members are not cloned using this method.
        /// </summary>
        /// <typeparam name="T">The type of object being copied.</typeparam>
        /// <param name="source">The object instance to copy.</param>
        /// <returns>The copied object.</returns>
        public static T CloneJson<T>(this T source)
        {
            // Don't serialize a null object, simply return the default for that object
            if (object.ReferenceEquals(source, null))
            {
                return default(T);
            }

            // initialize inner objects individually
            // for example in default constructor some list property initialized with some values,
            // but in 'source' these items are cleaned -
            // without ObjectCreationHandling.Replace default constructor values will be added to result
            var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };

            return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
        }
    }
}

namespace Schroders.Storage.MemoryDocumentStorage
{
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Linq;

    using Schroders.Storage.Core;

    public class MemoryDocumentStorage : IDocumentStorage
    {
        private readonly ConcurrentDictionary<string, DocumentCollection<object>> innerStorage = new ConcurrentDictionary<string, DocumentCollection<object>>();

        public void StoreDocument<TDocument>(string collectionName, string id, TDocument document)
        {
            var collection = this.innerStorage.GetOrAdd(collectionName, new DocumentCollection<object>());
            var documentCopy = document.CloneJson();
            collection[id] = documentCopy;
        }

        public IEnumerable<TDocument> GetAllDocuments<TDocument>(string collectionName)
        {
            DocumentCollection<object> collection;
            return !this.innerStorage.TryGetValue(collectionName, out collection) ? new TDocument[] { } : collection.Values.OfType<TDocument>().Select(d => d.CloneJson());
        }

        public TDocument GetDocument<TDocument>(string collectionName, string id)
        {
            DocumentCollection<object> collection;
            if (!this.innerStorage.TryGetValue(collectionName, out collection) || !collection.ContainsKey(id))
            {
                return default(TDocument);
            }

            return ((TDocument)collection[id]).CloneJson();
        }

        public void DeleteDocument<TDocument>(string collectionName, string id)
        {
            var collection = this.innerStorage.GetOrAdd(collectionName, new DocumentCollection<object>());
            collection.Remove(id);
        }
    }
}
namespace Schroders.Storage.MemoryDocumentStorage
{
    using System.Collections.Generic;

    public class DocumentCollection<TDocument> : Dictionary<string, TDocument>
    {        
    }
}
{
  "version": "1.0.0-*",

  "dependencies": {
    "CouchbaseNetClient": "2.2.6",
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
    "Schroders.Storage.Core": "1.0.0-*"
  },

  "frameworks": {
    "net461": {}
  }
}

using System;
using System.Collections.Generic;
using System.Linq;
using Couchbase;
using Couchbase.Configuration.Client;
using Couchbase.Core;
using Couchbase.N1QL;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using Schroders.Storage.Core;
using Schroders.Storage.CouchbaseDocumentStorage.Configurations;

namespace Schroders.Storage.CouchbaseDocumentStorage
{
    public class CouchbaseStorage : IDocumentStorage, IDisposable
    {
        private IBucket bucket;
        private readonly CouchbaseStorageConfiguration couchbaseStorageConfiguration;
        private readonly object thisLock = new object();

        public CouchbaseStorage(IOptions<CouchbaseStorageConfiguration> couchBaseSettings)
        {
            this.couchbaseStorageConfiguration = couchBaseSettings.Value;
        }

        public void StoreDocument<TDocument>(string collectionName, string id, TDocument document)
        {
            Initialize();

            var storageObject = JObject.FromObject(document);
            storageObject.Add("type", collectionName);

            var documentId = GetDocumentId(collectionName, id);
            var doc = new Document<JObject>
            {
                Id = documentId,
                Content = storageObject
            };

            var result = bucket.Upsert(doc);
            if (!result.Success)
            {
                throw new Exception(result.Message);
            }
        }

        public IEnumerable<TDocument> GetAllDocuments<TDocument>(string collectionName)
        {
            Initialize();

            var queryRequest = new QueryRequest()
                .Statement($"select {bucket.Name}.* from {bucket.Name} where type = $collectionName")
                .AddNamedParameter("collectionName", collectionName)
                .ScanConsistency(ScanConsistency.RequestPlus);

            var result = bucket.Query<TDocument>(queryRequest);
            return result.Rows;
        }

        public TDocument GetDocument<TDocument>(string collectionName, string id)
        {
            Initialize();

            var documentId = GetDocumentId(collectionName, id);

            var doc = bucket.GetDocument<TDocument>(documentId);
            if (doc == null)
            {
                return default(TDocument);
            }

            return doc.Content;
        }

        public void DeleteDocument<TDocument>(string collectionName, string id)
        {
            Initialize();

            var documentId = GetDocumentId(collectionName, id);
            bucket.Remove(documentId);
        }

        public void Dispose()
        {
            if (bucket != null)
            {
                bucket.Dispose();
                bucket = null;
            }
        }

        private static string GetDocumentId(string collectionName, string id)
        {
            return $"{collectionName}_{id}";
        }

        private void Initialize()
        {
            if (bucket != null)
            {
                return;
            }

            lock (thisLock)
            {
                if (bucket != null)
                {
                    return;
                }

                var config = new ClientConfiguration
                {
                    Servers = couchbaseStorageConfiguration.ServerUrl.Select(x => new Uri(x)).ToList()
                };

                var cluster = new Cluster(config);
                bucket = cluster.OpenBucket(couchbaseStorageConfiguration.BucketName);
            }
        }
    }
}

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Schroders.Storage.Core;
using Schroders.Storage.CouchbaseDocumentStorage.Configurations;

namespace Schroders.Storage.CouchbaseDocumentStorage.Extensions
{
    public static class CouchbaseStorageServiceCollectionExtensions
    {
        public static void AddCouchbaseStorage(this IServiceCollection services, IConfigurationRoot configuration)
        {
            services.AddSingleton<IDocumentStorage, CouchbaseStorage>();

            var section = configuration.GetSection(nameof(CouchbaseStorageConfiguration));
            services.Configure<CouchbaseStorageConfiguration>(section);
        }
    }
    
}
using System.Collections.Generic;

namespace Schroders.Storage.CouchbaseDocumentStorage.Configurations
{
    public class CouchbaseStorageConfiguration
    {
        public List<string> ServerUrl { get; set; }
        public string BucketName { get; set; }
    }
}

{
  "version": "1.0.0-*",

  "dependencies": {
  },

  "frameworks": {
    "net461": {
      "dependencies": {
      }
    }
  }
}

namespace Schroders.Storage.Core
{
    using System.Collections.Generic;

    public interface IDocumentStorage
    {
        void StoreDocument<TDocument>(string collectionName, string id, TDocument document);

        IEnumerable<TDocument> GetAllDocuments<TDocument>(string collectionName);

        TDocument GetDocument<TDocument>(string collectionName, string id);

        void DeleteDocument<TDocument>(string collectionName, string id);
    }
}

{
  "version": "1.0.0-*",

  "dependencies": {
    "Autofac": "4.1.0",
    "NServiceBus": "6.0.0",
    "NServiceBus.Autofac": "6.0.0",
    "Microsoft.Extensions.DependencyInjection.Abstractions": "1.1.0",
    "Microsoft.Extensions.Configuration.Abstractions": "1.1.0",
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
    "Schroders.Bus.Core": "1.0.0-*",
    "Microsoft.Extensions.Logging.Abstractions": "1.1.0"
  },

  "frameworks": {
    "net461": {}
  }
}
namespace Schroders.Bus.NServiceBus
{
    using System;

    using global::NServiceBus;

    public class NServiceBusInstance
    {
        public NServiceBusInstance(string name, IEndpointInstance instance)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException(nameof(name));
            }

            if (instance == null)
            {
                throw new ArgumentNullException(nameof(instance));
            }

            this.Name = name;
            this.Instance = instance;
        }

        public string Name { get; }

        public IEndpointInstance Instance { get; }
    }
}

namespace Schroders.Bus.NServiceBus
{
    using System;

    using global::NServiceBus;

    using Schroders.Bus.Core;
    using Schroders.Bus.Core.Contracts;

    using IBus = Schroders.Bus.Core.Contracts.IBus;

    public class NServiceBus : IBus
    {
        private readonly IEndpointInstanceProvider endpointInstanceProvider;

        public NServiceBus(IEndpointInstanceProvider endpointInstanceProvider)
        {
            this.endpointInstanceProvider = endpointInstanceProvider;
        }

        public void Send(BusMessage message)
        {
            var endpoint = this.endpointInstanceProvider.Get(message.TopicName);
            if (endpoint == null)
            {
                throw new Exception($"Failed to resolve NServiceBus endpoint mapping to send topic {message.TopicName}");
            }

            endpoint.Instance.Send(endpoint.Name, message);
        }

        public void Batch(IBusOperation[] operations)
        {
            throw new NotImplementedException();
        }

        public void Publish(string queueName, BusEvent message)
        {
            var endpoint = this.endpointInstanceProvider.Get(queueName);
            if (endpoint == null)
            {
                throw new Exception($"Failed to resolve NServiceBus endpoint mapping to publishing topic {message.TopicName}");
            }

            endpoint.Instance.Publish(message);
        }

        public BusSubscription Subscribe(string queueName, Func<BusContext, BusMessage, BusHandlerResponse> handler)
        {
            throw new NotImplementedException();
        }

        public void Unsubscribe(BusSubscription subscription)
        {
            throw new NotImplementedException();
        }
    }
}
namespace Schroders.Bus.NServiceBus
{
    public interface IEndpointInstanceProvider
    {
        NServiceBusInstance Get(string instanceName);
    }
}

namespace Schroders.Bus.NServiceBus
{
    using System;
    using System.Collections;
    using System.Collections.Generic;

    using global::NServiceBus;

    public class EndpointInstanceProvider : IEndpointInstanceProvider, IEnumerable, IDisposable
    {
        private readonly IDictionary<string, NServiceBusInstance> topicToInstanceMapping = new Dictionary<string, NServiceBusInstance>();

        public NServiceBusInstance Get(string instanceName)
        {
            return this.topicToInstanceMapping.ContainsKey(instanceName) ? this.topicToInstanceMapping[instanceName] : null;
        }

        public IEnumerator GetEnumerator()
        {
            throw new System.NotImplementedException();
        }

        public void Add(string topic, string endpointName, IEndpointInstance endpointInstance)
        {
            this.topicToInstanceMapping[topic] = new NServiceBusInstance(endpointName, endpointInstance);
        }

        public void Dispose()
        {
            foreach (var endpoint in this.topicToInstanceMapping.Values)
            {
                endpoint.Instance.Stop().ConfigureAwait(false).GetAwaiter().GetResult();
            }
        }
    }
}

namespace Schroders.Bus.NServiceBus
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq;
    using System.Threading.Tasks;

    using global::NServiceBus;

    using Schroders.Bus.Core;
    using Schroders.Bus.Core.Contracts;

    using IBus = Schroders.Bus.Core.Contracts.IBus;

    public class BaseNServiceBusHandler<TMessage> : IHandleMessages<TMessage>
    {
        private readonly IBusHandlerProvider busHandlerProvider;

        private readonly IBus bus;

        [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "nService bus is a name")]
        private readonly Func<TMessage, BusMessage> nServiceBusToMessageFunc;

        private readonly Func<BusMessage, TMessage> messageToNServiceBusFunc;

        private readonly Action<Exception> exceptionAction;

        /// <summary>
        /// Creates generic NServiceBus handler with capability to transform generic message to concrete one and back so we keep independence 
        /// from NService bus
        /// </summary>
        /// <param name="busHandlerProvider">Message handler provider</param>
        /// <param name="bus">Generic Bus</param>
        /// <param name="nServiceBusToMessageFunc">Conversion function from NServiceBus type to generic message</param>
        /// <param name="messageToNServiceBusFunc">Conversion function from generic message to concrete NServiceBus type</param>
        /// <param name="exceptionAction">Function that is called if handler exception happens, by default simply re-throws.
        /// Multiple calls can be done if more than 1 exception is received</param>
        public BaseNServiceBusHandler(
            IBusHandlerProvider busHandlerProvider,
            IBus bus,
            Func<TMessage, BusMessage> nServiceBusToMessageFunc,
            Func<BusMessage, TMessage> messageToNServiceBusFunc,
            Action<Exception> exceptionAction = null)
        {
            this.busHandlerProvider = busHandlerProvider;
            this.bus = bus;
            this.nServiceBusToMessageFunc = nServiceBusToMessageFunc;
            this.messageToNServiceBusFunc = messageToNServiceBusFunc;
            this.exceptionAction = exceptionAction ?? (e => { throw e; });
        }

        public Task Handle(TMessage message, IMessageHandlerContext context)
        {
            var busMessage = this.Convert(message);

            var handlers = this.busHandlerProvider.GetHandlers().Where(bh => bh.CanHandleMessage(busMessage));

            var busContext = new BusContext(this.bus);

            try
            {
                Task.WaitAll(handlers.Select(busHandler => Task.Run(() => busHandler.HandleMessage(busContext, busMessage))).Cast<Task>().ToArray());
            }
            catch (AggregateException ae)
            {
                foreach (var e in ae.Flatten().InnerExceptions)
                {
                    this.exceptionAction(e);
                }
            }

            return Task.CompletedTask;
        }

        protected virtual BusMessage Convert(TMessage fromObject)
        {
            return this.nServiceBusToMessageFunc(fromObject);
        }

        protected virtual TMessage Convert(BusMessage fromObject)
        {
            return this.messageToNServiceBusFunc(fromObject);
        }
    }
}


namespace Schroders.Bus.NServiceBus.Helpers
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;

    using Autofac;

    using global::NServiceBus;

    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Logging;

    using Schroders.Bus.NServiceBus.Configurations;
    using Schroders.Bus.NServiceBus.Extensions;

    public class NServiceBusEndpointHosting
    {
        [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "NServiceBus again here")]
        public static void StartConfiguredEndpoints(
            IConfigurationRoot configurationRoot, 
            IContainer container, 
            IDictionary<string, string[]> endpointToTopicMapping,
            Action<EndpointConfiguration> allEndpointSetupOptions = null,
            IDictionary<string, Action<EndpointConfiguration>> setupEndpointOptions = null)
        {
            var endpointInstanceProvider = container.Resolve<IEndpointInstanceProvider>() as EndpointInstanceProvider;
            if (endpointInstanceProvider == null)
            {
                throw new Exception("Could not resolve IEndpointInstanceProvider from given container. Make sure its registered with AddNServiceBus or manually.");
            }

            var section = configurationRoot.GetSection(nameof(NServiceBusConfiguration));
            var nServiceBusConfiguration = new NServiceBusConfiguration();
            section.Bind(nServiceBusConfiguration);

            var busEndpointConfigurations = nServiceBusConfiguration.Endpoints;
            foreach (var busEndpointOptions in busEndpointConfigurations)
            {
                var endpointConfiguration = new EndpointConfiguration(busEndpointOptions.Name);
                endpointConfiguration.SendFailedMessagesTo(busEndpointOptions.FailedMessagesQueueName);
                endpointConfiguration.UseSerialization<JsonSerializer>();
                endpointConfiguration.EnableInstallers();
                endpointConfiguration.UsePersistence<InMemoryPersistence>();
                endpointConfiguration.UseContainer<AutofacBuilder>(
                    customizations => customizations.ExistingLifetimeScope(container));

                allEndpointSetupOptions?.Invoke(endpointConfiguration);
                if (setupEndpointOptions != null && setupEndpointOptions.ContainsKey(busEndpointOptions.Name))
                {
                    var setupFunc = setupEndpointOptions[busEndpointOptions.Name];
                    setupFunc?.Invoke(endpointConfiguration);
                }

                var endpointInstance = Endpoint.Start(endpointConfiguration).ConfigureAwait(false).GetAwaiter().GetResult();
                if (endpointToTopicMapping.ContainsKey(busEndpointOptions.Name))
                {
                    var topicNames = endpointToTopicMapping[busEndpointOptions.Name];
                    foreach (var topicName in topicNames)
                    {
                        endpointInstanceProvider.Add(topicName, busEndpointOptions.Name, endpointInstance);
                    }
                }
                else
                {
                    // Todo: throw warning that no mapping exist to an endpoint (maybe no need for handler only)
                    var loggerFactory = container.Resolve<ILoggerFactory>();
                    var logger = loggerFactory.CreateLogger(typeof(NServiceBusServiceCollectionExtensions));
                    logger.LogWarning($"Endpoint instance {busEndpointOptions.Name} does not have mapping to topics. Make sure you did not forget to add mappings.");
                }
            }
        }
    }

}
namespace Schroders.Bus.NServiceBus.Extensions
{
    using Microsoft.Extensions.DependencyInjection;

    using Schroders.Bus.Core;
    using Schroders.Bus.Core.Contracts;
    using Schroders.Bus.NServiceBus;

    public static class NServiceBusServiceCollectionExtensions
    {
        public static void AddNServiceBus(this IServiceCollection services)
        {
            services.AddSingleton<IBusHandlerProvider, DefaultBusHandlerProvider>();
            services.AddSingleton<Schroders.Bus.Core.Contracts.IBus, NServiceBus>();

            services.AddSingleton<IEndpointInstanceProvider, EndpointInstanceProvider>();
        }
    }
}
namespace Schroders.Bus.NServiceBus.Configurations
{
    using System;
    using System.Linq;    

    public class NServiceBusConfiguration
    {
        public BusEndpointConfiguration[] Endpoints { get; set; }

        public BusEndpointConfiguration GetByName(string name)
        {
            var endpointConfiguration = this.Endpoints.FirstOrDefault(ep => ep.Name.Equals(name, StringComparison.InvariantCulture));
            if (endpointConfiguration == null)
            {
                throw new ArgumentOutOfRangeException(nameof(name), name, "Configuration with given name was not found. Check if it is defined in configuration");
            }

            return endpointConfiguration;
        }
    }
}


namespace Schroders.Bus.NServiceBus.Configurations
{
    using System;

    public class BusEndpointConfiguration
    {
        public string Name { get; set; }

        public string FailedMessagesQueueName { get; set; }
    }
}

{
  "version": "1.0.0-*",

  "dependencies": {
    "Schroders.Bus.Core": "1.0.0-*"
  },

  "frameworks": {
    "net461": {
      "dependencies": {
      }
    }
  }
}
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using Schroders.Bus.Core;
using Schroders.Bus.Core.Contracts;
using System.Linq;

namespace Schroders.Bus.MemoryBus
{
    public class MemoryBus : IBus
    {
        private readonly List<IBusHandler> busHandlers;

        private readonly List<BusSubscription> busSubscribers = new List<BusSubscription>();

        public MemoryBus(IBusHandlerProvider busHandlerProvider)
        {
            this.busHandlers = new List<IBusHandler>(busHandlerProvider.GetHandlers());
        }

        public void Send(BusMessage message)
        {
            var handlers = this.busHandlers.FindAll(bh => bh.CanHandleMessage(message));

            var busContext = new BusContext(this);

            try
            {
                Task.WaitAll(handlers.Select(busHandler => Task.Run(() => busHandler.HandleMessage(busContext, message))).Cast<Task>().ToArray());
            }
            catch (AggregateException ae)
            {
                throw ae.Flatten();
            }
        }

        public void Batch(IBusOperation[] operations)
        {
            throw new NotImplementedException();
        }

        public void Publish(string queueName, BusEvent message)
        {
            var subscribers = this.busSubscribers.FindAll(bs => bs.TopicName == queueName);

            var busContext = new BusContext(this);
            var busMessage = new BusMessage
            {
                Payload = message.Payload,
                TopicName = message.TopicName
            };

            try
            {
                Task.WaitAll(subscribers.Select(busHandler => Task.Run(() => busHandler.Handler(busContext, busMessage))).Cast<Task>().ToArray());
            }
            catch (AggregateException ae)
            {
                throw ae.Flatten();
            }
        }

        public BusSubscription Subscribe(string queueName, Func<BusContext, BusMessage, BusHandlerResponse> handler)
        {
            var subscription = new BusSubscription { TopicName = queueName, Handler = handler };

            this.busSubscribers.Add(subscription);

            return subscription;
        }

        public void Unsubscribe(BusSubscription subscription)
        {
            this.busSubscribers.Remove(subscription);
        }
    }
}

{
  "version": "1.0.0-*",

  "dependencies": {
  },

  "frameworks": {
    "net461": {
      "dependencies": {
      }
    }
  }
}
namespace Schroders.Bus.Core
{
    using System.Collections.Generic;
    using System.Collections.ObjectModel;

    using Schroders.Bus.Core.Contracts;

    public class EmptyMessageContext : IBusMessageContext
    {
        public static readonly IBusMessageContext Instance = new EmptyMessageContext();

        public EmptyMessageContext()
        {
            this.Username = null;
            this.RequestId = null;
            this.Values = new ReadOnlyDictionary<object, object>(new Dictionary<object, object>());
        }

        public string Username { get; }

        public string RequestId { get; }

        public IDictionary<object, object> Values { get; }
    }
}
using System.Collections.Generic;
using Schroders.Bus.Core.Contracts;

namespace Schroders.Bus.Core
{
    public class DefaultBusHandlerProvider : IBusHandlerProvider
    {
        private readonly IEnumerable<IBusHandler> handlers;

        public DefaultBusHandlerProvider(IEnumerable<IBusHandler> handlers)
        {
            this.handlers = handlers;
        }

        public IEnumerable<IBusHandler> GetHandlers()
        {
            return this.handlers;
        }
    }
}

using System;

namespace Schroders.Bus.Core
{
    public class BusSubscription
    {
        public string TopicName { get; set; }

        public Func<BusContext, BusMessage, BusHandlerResponse> Handler { get; set; }
    }
}

namespace Schroders.Bus.Core
{
    public enum BusOperationType
    {
        Send,
        Publish
    }
}

namespace Schroders.Bus.Core
{
    using System.Collections.Generic;

    using Schroders.Bus.Core.Contracts;

    public class BusMessageContext : IBusMessageContext
    {            
        public BusMessageContext()
        {
            this.Values = new Dictionary<object, object>();
        }

        public string Username { get; set; }

        public string RequestId { get; set; }

        public IDictionary<object, object> Values { get; }
    }
}
namespace Schroders.Bus.Core
{
    using Schroders.Bus.Core.Contracts;

    public class BusMessage
    {
        public BusMessage()
        {
            this.MessageContext = new BusMessageContext();
        }

        public string TopicName { get; set; }

        public object Payload { get; set; }

        public IBusMessageContext MessageContext { get; set; }
    }
}

namespace Schroders.Bus.Core
{
    public class BusHandlerResponse
    {
    }
}

namespace Schroders.Bus.Core
{
    public class BusEvent
    {
        public string TopicName { get; set; }

        public object Payload { get; set; }
    }
}

namespace Schroders.Bus.Core
{
    using Schroders.Bus.Core.Contracts;

    public class BusContext
    {
        public BusContext(IBus bus)
        {
            this.Bus = bus;
        }

        public IBus Bus { get; }
    }
}

namespace Schroders.Bus.Core.Contracts
{
    public interface IBusOperation
    {
        BusOperationType OperationType { get; set; }

        BusMessage Message { get; set; }
    }
}


namespace Schroders.Bus.Core.Contracts
{
    using System.Collections.Generic;

    public interface IBusMessageContext
    {
        string Username { get; }

        string RequestId { get; }

        IDictionary<object, object> Values { get; }
    }
}
using System.Collections.Generic;

namespace Schroders.Bus.Core.Contracts
{
    public interface IBusHandlerProvider
    {
        IEnumerable<IBusHandler> GetHandlers();
    }
}


namespace Schroders.Bus.Core.Contracts
{
    public interface IBusHandler
    {
        bool CanHandleMessage(BusMessage message);

        BusHandlerResponse HandleMessage(BusContext busContext, BusMessage busMessage);
    }
}

Comments