@ -0,0 +1,59 @@ | |||||
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base | |||||
WORKDIR /app | |||||
EXPOSE 80 | |||||
FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build | |||||
WORKDIR /src | |||||
# Keep the project list and command dotnet restore identical in all Dockfiles to maximize image cache utilization | |||||
COPY eShopOnContainers-ServicesAndWebApps.sln . | |||||
COPY docker-compose.dcproj /src/ | |||||
COPY src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj src/ApiGateways/ApiGw-Base/ | |||||
COPY src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj src/ApiGateways/Mobile.Bff.Shopping/aggregator/ | |||||
COPY src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj src/ApiGateways/Web.Bff.Shopping/aggregator/ | |||||
COPY src/BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj src/BuildingBlocks/Devspaces.Support/ | |||||
COPY src/BuildingBlocks/EventBus/EventBus/EventBus.csproj src/BuildingBlocks/EventBus/EventBus/ | |||||
COPY src/BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj src/BuildingBlocks/EventBus/EventBus.Tests/ | |||||
COPY src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj src/BuildingBlocks/EventBus/EventBusRabbitMQ/ | |||||
COPY src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj src/BuildingBlocks/EventBus/EventBusServiceBus/ | |||||
COPY src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj src/BuildingBlocks/EventBus/IntegrationEventLogEF/ | |||||
COPY src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj src/BuildingBlocks/WebHostCustomization/WebHost.Customization/ | |||||
COPY src/Services/Basket/Basket.API/Basket.API.csproj src/Services/Basket/Basket.API/ | |||||
COPY src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj src/Services/Basket/Basket.FunctionalTests/ | |||||
COPY src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj src/Services/Basket/Basket.UnitTests/ | |||||
COPY src/Services/Catalog/Catalog.API/Catalog.API.csproj src/Services/Catalog/Catalog.API/ | |||||
COPY src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj src/Services/Catalog/Catalog.FunctionalTests/ | |||||
COPY src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj src/Services/Catalog/Catalog.UnitTests/ | |||||
COPY src/Services/Identity/Identity.API/Identity.API.csproj src/Services/Identity/Identity.API/ | |||||
COPY src/Services/Location/Locations.API/Locations.API.csproj src/Services/Location/Locations.API/ | |||||
COPY src/Services/Location/Locations.FunctionalTests/Locations.FunctionalTests.csproj src/Services/Location/Locations.FunctionalTests/ | |||||
COPY src/Services/Marketing/Marketing.API/Marketing.API.csproj src/Services/Marketing/Marketing.API/ | |||||
COPY src/Services/Marketing/Marketing.FunctionalTests/Marketing.FunctionalTests.csproj src/Services/Marketing/Marketing.FunctionalTests/ | |||||
COPY src/Services/Ordering/Ordering.API/Ordering.API.csproj src/Services/Ordering/Ordering.API/ | |||||
COPY src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj src/Services/Ordering/Ordering.BackgroundTasks/ | |||||
COPY src/Services/Ordering/Ordering.Domain/Ordering.Domain.csproj src/Services/Ordering/Ordering.Domain/ | |||||
COPY src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj src/Services/Ordering/Ordering.FunctionalTests/ | |||||
COPY src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj src/Services/Ordering/Ordering.Infrastructure/ | |||||
COPY src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj src/Services/Ordering/Ordering.SignalrHub/ | |||||
COPY src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj src/Services/Ordering/Ordering.UnitTests/ | |||||
COPY src/Services/Payment/Payment.API/Payment.API.csproj src/Services/Payment/Payment.API/ | |||||
COPY src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj src/Services/Webhooks/Webhooks.API/ | |||||
COPY src/Web/WebhookClient/WebhookClient.csproj src/Web/WebhookClient/ | |||||
COPY src/Web/WebMVC/WebMVC.csproj src/Web/WebMVC/ | |||||
COPY src/Web/WebSPA/WebSPA.csproj src/Web/WebSPA/ | |||||
COPY src/Web/WebStatus/WebStatus.csproj src/Web/WebStatus/ | |||||
COPY test/ServicesTests/Application.FunctionalTests/Application.FunctionalTests.csproj test/ServicesTests/Application.FunctionalTests/ | |||||
COPY test/ServicesTests/LoadTest/LoadTest.csproj test/ServicesTests/LoadTest/ | |||||
RUN dotnet restore eShopOnContainers-ServicesAndWebApps.sln | |||||
COPY . . | |||||
WORKDIR /src/src/Services/TenantCustomisations/TenantAShippingInformation | |||||
RUN dotnet publish --no-restore -c Release -o /app | |||||
FROM build AS publish | |||||
FROM base AS final | |||||
WORKDIR /app | |||||
COPY --from=publish /app . | |||||
ENTRYPOINT ["dotnet", "TenantAShippingInformation.dll"] |
@ -0,0 +1,28 @@ | |||||
using Autofac; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using System.Reflection; | |||||
using TenantAShippingInformation.IntegrationEvents.EventHandling; | |||||
namespace TenantAShippingInformation.Infrastructure.AutofacModules | |||||
{ | |||||
public class ApplicationModule | |||||
:Autofac.Module | |||||
{ | |||||
public string QueriesConnectionString { get; } | |||||
public ApplicationModule(string qconstr) | |||||
{ | |||||
QueriesConnectionString = qconstr; | |||||
} | |||||
protected override void Load(ContainerBuilder builder) | |||||
{ | |||||
builder.RegisterAssemblyTypes(typeof(OrderStatusChangedToAwaitingValidationIntegrationEventHandler).GetTypeInfo().Assembly) | |||||
.AsClosedTypesOf(typeof(IIntegrationEventHandler<>)); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
using Autofac; | |||||
namespace TenantAShippingInformation.Infrastructure.AutofacModules | |||||
{ | |||||
public class MediatorModule : Autofac.Module | |||||
{ | |||||
protected override void Load(ContainerBuilder builder) | |||||
{ | |||||
//TODO | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,33 @@ | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Swashbuckle.AspNetCore.Swagger; | |||||
using Swashbuckle.AspNetCore.SwaggerGen; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace TenantAShippingInformation.Infrastructure.Filters | |||||
{ | |||||
public class AuthorizeCheckOperationFilter : IOperationFilter | |||||
{ | |||||
public void Apply(Operation operation, OperationFilterContext context) | |||||
{ | |||||
// Check for authorize attribute | |||||
var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any() || | |||||
context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any(); | |||||
if (!hasAuthorize) return; | |||||
operation.Responses.TryAdd("401", new Response { Description = "Unauthorized" }); | |||||
operation.Responses.TryAdd("403", new Response { Description = "Forbidden" }); | |||||
operation.Security = new List<IDictionary<string, IEnumerable<string>>> | |||||
{ | |||||
new Dictionary<string, IEnumerable<string>> | |||||
{ | |||||
{ "oauth2", new [] { "orderingapi" } } | |||||
} | |||||
}; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,69 @@ | |||||
namespace TenantAShippingInformation.Infrastructure.Filters | |||||
{ | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.AspNetCore.Http; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.AspNetCore.Mvc.Filters; | |||||
using Microsoft.Extensions.Logging; | |||||
using System.Net; | |||||
public class HttpGlobalExceptionFilter : IExceptionFilter | |||||
{ | |||||
private readonly IHostingEnvironment env; | |||||
private readonly ILogger<HttpGlobalExceptionFilter> logger; | |||||
public HttpGlobalExceptionFilter(IHostingEnvironment env, ILogger<HttpGlobalExceptionFilter> logger) | |||||
{ | |||||
this.env = env; | |||||
this.logger = logger; | |||||
} | |||||
public void OnException(ExceptionContext context) | |||||
{ | |||||
logger.LogError(new EventId(context.Exception.HResult), | |||||
context.Exception, | |||||
context.Exception.Message); | |||||
if (1==2)//TODO | |||||
{ | |||||
var problemDetails = new ValidationProblemDetails() | |||||
{ | |||||
Instance = context.HttpContext.Request.Path, | |||||
Status = StatusCodes.Status400BadRequest, | |||||
Detail = "Please refer to the errors property for additional details." | |||||
}; | |||||
problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() }); | |||||
context.Result = new BadRequestObjectResult(problemDetails); | |||||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; | |||||
} | |||||
else | |||||
{ | |||||
var json = new JsonErrorResponse | |||||
{ | |||||
Messages = new[] { "An error occur.Try it again." } | |||||
}; | |||||
if (env.IsDevelopment()) | |||||
{ | |||||
json.DeveloperMessage = context.Exception; | |||||
} | |||||
// Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 | |||||
// It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information | |||||
//TODO | |||||
//context.Result = new InternalServerErrorObjectResult(json); | |||||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; | |||||
} | |||||
context.ExceptionHandled = true; | |||||
} | |||||
private class JsonErrorResponse | |||||
{ | |||||
public string[] Messages { get; set; } | |||||
public object DeveloperMessage { get; set; } | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,34 @@ | |||||
using Microsoft.AspNetCore.SignalR; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Microsoft.Extensions.Logging; | |||||
using Serilog.Context; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Diagnostics; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using TenantAShippingInformation.IntegrationEvents.Events; | |||||
namespace TenantAShippingInformation.IntegrationEvents.EventHandling | |||||
{ | |||||
public class OrderStatusChangedToAwaitingValidationIntegrationEventHandler : | |||||
IIntegrationEventHandler<OrderStatusChangedToAwaitingValidationIntegrationEvent> | |||||
{ | |||||
private readonly ILogger<OrderStatusChangedToAwaitingValidationIntegrationEventHandler> _logger; | |||||
public OrderStatusChangedToAwaitingValidationIntegrationEventHandler(ILogger<OrderStatusChangedToAwaitingValidationIntegrationEventHandler> logger) | |||||
{ | |||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||||
} | |||||
public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent @event) | |||||
{ | |||||
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}- TenantA")) | |||||
{ | |||||
//TODO | |||||
Debug.WriteLine(@event); | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,30 @@ | |||||
using System.Collections.Generic; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
namespace TenantAShippingInformation.IntegrationEvents.Events | |||||
{ | |||||
public class OrderStatusChangedToAwaitingValidationIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public IEnumerable<OrderStockItem> OrderStockItems { get; } | |||||
public OrderStatusChangedToAwaitingValidationIntegrationEvent(int orderId, | |||||
IEnumerable<OrderStockItem> orderStockItems) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderStockItems = orderStockItems; | |||||
} | |||||
} | |||||
public class OrderStockItem | |||||
{ | |||||
public int ProductId { get; } | |||||
public int Units { get; } | |||||
public OrderStockItem(int productId, int units) | |||||
{ | |||||
ProductId = productId; | |||||
Units = units; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,87 @@ | |||||
using Microsoft.AspNetCore; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Serilog; | |||||
using System; | |||||
using System.IO; | |||||
namespace TenantAShippingInformation | |||||
{ | |||||
public class Program | |||||
{ | |||||
public static readonly string Namespace = typeof(Program).Namespace; | |||||
public static readonly string AppName = Namespace; | |||||
public static int Main(string[] args) | |||||
{ | |||||
var configuration = GetConfiguration(); | |||||
Log.Logger = CreateSerilogLogger(configuration); | |||||
try | |||||
{ | |||||
Log.Information("Configuring web host ({ApplicationContext})...", AppName); | |||||
var host = BuildWebHost(configuration, args); | |||||
Log.Information("Starting web host ({ApplicationContext})...", AppName); | |||||
host.Run(); | |||||
return 0; | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); | |||||
return 1; | |||||
} | |||||
finally | |||||
{ | |||||
Log.CloseAndFlush(); | |||||
} | |||||
} | |||||
private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => | |||||
WebHost.CreateDefaultBuilder(args) | |||||
.CaptureStartupErrors(false) | |||||
.UseStartup<Startup>() | |||||
.UseApplicationInsights() | |||||
.UseContentRoot(Directory.GetCurrentDirectory()) | |||||
.UseConfiguration(configuration) | |||||
.UseSerilog() | |||||
.Build(); | |||||
private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) | |||||
{ | |||||
var seqServerUrl = configuration["Serilog:SeqServerUrl"]; | |||||
var logstashUrl = configuration["Serilog:LogstashgUrl"]; | |||||
return new LoggerConfiguration() | |||||
.MinimumLevel.Verbose() | |||||
.Enrich.WithProperty("ApplicationContext", AppName) | |||||
.Enrich.FromLogContext() | |||||
.WriteTo.Console() | |||||
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) | |||||
.WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl) | |||||
.ReadFrom.Configuration(configuration) | |||||
.CreateLogger(); | |||||
} | |||||
private static IConfiguration GetConfiguration() | |||||
{ | |||||
var builder = new ConfigurationBuilder() | |||||
.SetBasePath(Directory.GetCurrentDirectory()) | |||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) | |||||
.AddEnvironmentVariables(); | |||||
var config = builder.Build(); | |||||
if (config.GetValue<bool>("UseVault", false)) | |||||
{ | |||||
builder.AddAzureKeyVault( | |||||
$"https://{config["Vault:Name"]}.vault.azure.net/", | |||||
config["Vault:ClientId"], | |||||
config["Vault:ClientSecret"]); | |||||
} | |||||
return builder.Build(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,27 @@ | |||||
{ | |||||
"iisSettings": { | |||||
"windowsAuthentication": false, | |||||
"anonymousAuthentication": true, | |||||
"iisExpress": { | |||||
"applicationUrl": "http://localhost:59299", | |||||
"sslPort": 44393 | |||||
} | |||||
}, | |||||
"profiles": { | |||||
"IIS Express": { | |||||
"commandName": "IISExpress", | |||||
"launchBrowser": true, | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
} | |||||
}, | |||||
"TenantAShippingInformation": { | |||||
"commandName": "Project", | |||||
"launchBrowser": true, | |||||
"applicationUrl": "https://localhost:5001;http://localhost:5000", | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,411 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Data.Common; | |||||
using System.IdentityModel.Tokens.Jwt; | |||||
using System.Reflection; | |||||
using Autofac; | |||||
using Autofac.Extensions.DependencyInjection; | |||||
using HealthChecks.UI.Client; | |||||
using Microsoft.ApplicationInsights.Extensibility; | |||||
using Microsoft.ApplicationInsights.ServiceFabric; | |||||
using Microsoft.AspNetCore.Authentication.JwtBearer; | |||||
using Microsoft.AspNetCore.Builder; | |||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks; | |||||
using Microsoft.AspNetCore.Http; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.Azure.ServiceBus; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Diagnostics.HealthChecks; | |||||
using Microsoft.Extensions.Logging; | |||||
using RabbitMQ.Client; | |||||
using Swashbuckle.AspNetCore.Swagger; | |||||
using TenantAShippingInformation.Infrastructure.AutofacModules; | |||||
using TenantAShippingInformation.Infrastructure.Filters; | |||||
namespace TenantAShippingInformation | |||||
{ | |||||
public class Startup | |||||
{ | |||||
public Startup(IConfiguration configuration) | |||||
{ | |||||
Configuration = configuration; | |||||
} | |||||
public IConfiguration Configuration { get; } | |||||
public IServiceProvider ConfigureServices(IServiceCollection services) | |||||
{ | |||||
services.AddApplicationInsights(Configuration) | |||||
.AddCustomMvc() | |||||
.AddHealthChecks(Configuration) | |||||
.AddCustomDbContext(Configuration) | |||||
.AddCustomSwagger(Configuration) | |||||
.AddCustomIntegrations(Configuration) | |||||
.AddCustomConfiguration(Configuration) | |||||
.AddEventBus(Configuration) | |||||
.AddCustomAuthentication(Configuration); | |||||
//configure autofac | |||||
var container = new ContainerBuilder(); | |||||
container.Populate(services); | |||||
container.RegisterModule(new MediatorModule()); | |||||
container.RegisterModule(new ApplicationModule(Configuration["ConnectionString"])); | |||||
return new AutofacServiceProvider(container.Build()); | |||||
} | |||||
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) | |||||
{ | |||||
//loggerFactory.AddAzureWebAppDiagnostics(); | |||||
//loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); | |||||
var pathBase = Configuration["PATH_BASE"]; | |||||
if (!string.IsNullOrEmpty(pathBase)) | |||||
{ | |||||
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase); | |||||
app.UsePathBase(pathBase); | |||||
} | |||||
app.UseCors("CorsPolicy"); | |||||
app.UseHealthChecks("/liveness", new HealthCheckOptions | |||||
{ | |||||
Predicate = r => r.Name.Contains("self") | |||||
}); | |||||
app.UseHealthChecks("/hc", new HealthCheckOptions() | |||||
{ | |||||
Predicate = _ => true, | |||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse | |||||
}); | |||||
ConfigureAuth(app); | |||||
app.UseMvcWithDefaultRoute(); | |||||
app.UseSwagger() | |||||
.UseSwaggerUI(c => | |||||
{ | |||||
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Ordering.API V1"); | |||||
c.OAuthClientId("orderingswaggerui"); | |||||
c.OAuthAppName("Ordering Swagger UI"); | |||||
}); | |||||
ConfigureEventBus(app); | |||||
//using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope()) | |||||
//{ | |||||
// var context = serviceScope.ServiceProvider.GetRequiredService<TenantAContext>(); | |||||
// context.Database.EnsureCreated(); | |||||
//} | |||||
} | |||||
private void ConfigureEventBus(IApplicationBuilder app) | |||||
{ | |||||
var eventBus = app.ApplicationServices.GetRequiredService<Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions.IEventBus>(); | |||||
//eventBus.Subscribe<UserCheckoutAcceptedIntegrationEvent, IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>>(); | |||||
//eventBus.Subscribe<OrderStatusChangedToSubmittedIntegrationEvent, OrderStatusChangedToSubmittedIntegrationEventHandler>(); | |||||
} | |||||
protected virtual void ConfigureAuth(IApplicationBuilder app) | |||||
{ | |||||
if (Configuration.GetValue<bool>("UseLoadTest")) | |||||
{ | |||||
//app.UseMiddleware<ByPassAuthMiddleware>(); | |||||
//TODO | |||||
} | |||||
app.UseAuthentication(); | |||||
} | |||||
} | |||||
static class CustomExtensionsMethods | |||||
{ | |||||
public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddApplicationInsightsTelemetry(configuration); | |||||
var orchestratorType = configuration.GetValue<string>("OrchestratorType"); | |||||
if (orchestratorType?.ToUpper() == "K8S") | |||||
{ | |||||
// Enable K8s telemetry initializer | |||||
services.AddApplicationInsightsKubernetesEnricher(); | |||||
} | |||||
if (orchestratorType?.ToUpper() == "SF") | |||||
{ | |||||
// Enable SF telemetry initializer | |||||
services.AddSingleton<ITelemetryInitializer>((serviceProvider) => | |||||
new FabricTelemetryInitializer()); | |||||
} | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomMvc(this IServiceCollection services) | |||||
{ | |||||
// Add framework services. | |||||
services.AddMvc(options => | |||||
{ | |||||
options.Filters.Add(typeof(HttpGlobalExceptionFilter)); | |||||
}) | |||||
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2) | |||||
.AddControllersAsServices(); //Injecting Controllers themselves thru DI | |||||
//For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services | |||||
services.AddCors(options => | |||||
{ | |||||
options.AddPolicy("CorsPolicy", | |||||
builder => builder | |||||
.SetIsOriginAllowed((host) => true) | |||||
.AllowAnyMethod() | |||||
.AllowAnyHeader() | |||||
.AllowCredentials()); | |||||
}); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
var hcBuilder = services.AddHealthChecks(); | |||||
hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); | |||||
hcBuilder | |||||
.AddSqlServer( | |||||
configuration["ConnectionString"], | |||||
name: "OrderingDB-check", | |||||
tags: new string[] { "orderingdb" }); | |||||
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | |||||
{ | |||||
hcBuilder | |||||
.AddAzureServiceBusTopic( | |||||
configuration["EventBusConnection"], | |||||
topicName: "eshop_event_bus", | |||||
name: "ordering-servicebus-check", | |||||
tags: new string[] { "servicebus" }); | |||||
} | |||||
else | |||||
{ | |||||
hcBuilder | |||||
.AddRabbitMQ( | |||||
$"amqp://{configuration["EventBusConnection"]}", | |||||
name: "ordering-rabbitmqbus-check", | |||||
tags: new string[] { "rabbitmqbus" }); | |||||
} | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
//services.AddDbContext<TenantAContext>(options => | |||||
// options.UseSqlServer(configuration["ConnectionString"])); | |||||
services.AddDbContext<IntegrationEventLogContext>(options => | |||||
{ | |||||
options.UseSqlServer(configuration["ConnectionString"], | |||||
sqlServerOptionsAction: sqlOptions => | |||||
{ | |||||
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); | |||||
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency | |||||
sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); | |||||
}); | |||||
}); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomSwagger(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddSwaggerGen(options => | |||||
{ | |||||
options.DescribeAllEnumsAsStrings(); | |||||
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info | |||||
{ | |||||
Title = "Ordering HTTP API", | |||||
Version = "v1", | |||||
Description = "The Ordering Service HTTP API", | |||||
TermsOfService = "Terms Of Service" | |||||
}); | |||||
options.AddSecurityDefinition("oauth2", new OAuth2Scheme | |||||
{ | |||||
Type = "oauth2", | |||||
Flow = "implicit", | |||||
AuthorizationUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize", | |||||
TokenUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token", | |||||
Scopes = new Dictionary<string, string>() | |||||
{ | |||||
{ "orders", "Ordering API" } | |||||
} | |||||
}); | |||||
options.OperationFilter<AuthorizeCheckOperationFilter>(); | |||||
}); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | |||||
//services.AddTransient<IIdentityService, IdentityService>(); | |||||
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>( | |||||
sp => (DbConnection c) => new IntegrationEventLogService(c)); | |||||
//services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>(); | |||||
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | |||||
{ | |||||
services.AddSingleton<IServiceBusPersisterConnection>(sp => | |||||
{ | |||||
var logger = sp.GetRequiredService<ILogger<DefaultServiceBusPersisterConnection>>(); | |||||
var serviceBusConnectionString = configuration["EventBusConnection"]; | |||||
var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); | |||||
return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger); | |||||
}); | |||||
} | |||||
else | |||||
{ | |||||
services.AddSingleton<IRabbitMQPersistentConnection>(sp => | |||||
{ | |||||
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>(); | |||||
var factory = new ConnectionFactory() | |||||
{ | |||||
HostName = configuration["EventBusConnection"], | |||||
DispatchConsumersAsync = true | |||||
}; | |||||
if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) | |||||
{ | |||||
factory.UserName = configuration["EventBusUserName"]; | |||||
} | |||||
if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) | |||||
{ | |||||
factory.Password = configuration["EventBusPassword"]; | |||||
} | |||||
factory.VirtualHost = "TenantA"; | |||||
var retryCount = 5; | |||||
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) | |||||
{ | |||||
retryCount = int.Parse(configuration["EventBusRetryCount"]); | |||||
} | |||||
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); | |||||
}); | |||||
} | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomConfiguration(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddOptions(); | |||||
//services.Configure<OrderingSettings>(configuration); | |||||
services.Configure<ApiBehaviorOptions>(options => | |||||
{ | |||||
options.InvalidModelStateResponseFactory = context => | |||||
{ | |||||
var problemDetails = new ValidationProblemDetails(context.ModelState) | |||||
{ | |||||
Instance = context.HttpContext.Request.Path, | |||||
Status = StatusCodes.Status400BadRequest, | |||||
Detail = "Please refer to the errors property for additional details." | |||||
}; | |||||
return new BadRequestObjectResult(problemDetails) | |||||
{ | |||||
ContentTypes = { "application/problem+json", "application/problem+xml" } | |||||
}; | |||||
}; | |||||
}); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
var subscriptionClientName = configuration["SubscriptionClientName"]; | |||||
/* if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | |||||
{ | |||||
services.AddSingleton<IEventBus, EventBusServiceBus>(sp => | |||||
{ | |||||
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>(); | |||||
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | |||||
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | |||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | |||||
return new EventBusServiceBus(serviceBusPersisterConnection, logger, | |||||
eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); | |||||
}); | |||||
} | |||||
else*/ | |||||
{ | |||||
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp => | |||||
{ | |||||
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>(); | |||||
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | |||||
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>(); | |||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | |||||
var retryCount = 5; | |||||
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) | |||||
{ | |||||
retryCount = int.Parse(configuration["EventBusRetryCount"]); | |||||
} | |||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, "TenantA", subscriptionClientName, retryCount); | |||||
}); | |||||
} | |||||
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); | |||||
//services.AddTransient<TenantAUserCheckoutAcceptedIntegrationEventHandler>(); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
// prevent from mapping "sub" claim to nameidentifier. | |||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); | |||||
var identityUrl = configuration.GetValue<string>("IdentityUrl"); | |||||
services.AddAuthentication(options => | |||||
{ | |||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; | |||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; | |||||
}).AddJwtBearer(options => | |||||
{ | |||||
options.Authority = identityUrl; | |||||
options.RequireHttpsMetadata = false; | |||||
options.Audience = "orders"; | |||||
}); | |||||
return services; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,53 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netcoreapp2.2</TargetFramework> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.0" /> | |||||
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.0" /> | |||||
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="2.2.0" /> | |||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.4" /> | |||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> | |||||
<PackageReference Include="Dapper" Version="1.50.7" /> | |||||
<PackageReference Include="FluentValidation.AspNetCore" Version="7.5.0" /> | |||||
<PackageReference Include="MediatR" Version="5.1.0" /> | |||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="5.1.0" /> | |||||
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.11.0" /> | |||||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> | |||||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" /> | |||||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.2" /> | |||||
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.2.2" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.NETCore.App" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.4" /> | |||||
<PackageReference Include="Polly" Version="6.0.1" /> | |||||
<PackageReference Include="Serilog" Version="2.9.0" /> | |||||
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" /> | |||||
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" /> | |||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" /> | |||||
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" /> | |||||
<PackageReference Include="Serilog.Sinks.Http" Version="4.2.1" /> | |||||
<PackageReference Include="Serilog.Sinks.Seq" Version="4.0.0" /> | |||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0" /> | |||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="4.0.1" /> | |||||
<PackageReference Include="System.Reflection" Version="4.3.0" /> | |||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\Devspaces.Support\Devspaces.Support.csproj" /> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" /> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" /> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\WebHostCustomization\WebHost.Customization\WebHost.Customization.csproj" /> | |||||
</ItemGroup> | |||||
</Project> |
@ -0,0 +1,9 @@ | |||||
{ | |||||
"Logging": { | |||||
"LogLevel": { | |||||
"Default": "Information", | |||||
"Microsoft": "Warning", | |||||
"Microsoft.Hosting.Lifetime": "Information" | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,29 @@ | |||||
{ | |||||
"Serilog": { | |||||
"SeqServerUrl": null, | |||||
"LogstashgUrl": null, | |||||
"MinimumLevel": { | |||||
"Default": "Information", | |||||
"Override": { | |||||
"Microsoft": "Warning", | |||||
"Microsoft.eShopOnContainers": "Information", | |||||
"System": "Warning" | |||||
} | |||||
} | |||||
}, | |||||
"IdentityUrl": "http://localhost:5105", | |||||
//"ConnectionString": "127.0.0.1", | |||||
"ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.TenantADb;User Id=sa;Password=Pass@word;", | |||||
"AzureServiceBusEnabled": false, | |||||
"SubscriptionClientName": "TenantAShippingInformation", | |||||
"ApplicationInsights": { | |||||
"InstrumentationKey": "" | |||||
}, | |||||
"EventBusRetryCount": 5, | |||||
"UseVault": false, | |||||
"Vault": { | |||||
"Name": "eshop", | |||||
"ClientId": "your-clien-id", | |||||
"ClientSecret": "your-client-secret" | |||||
} | |||||
} |