@ -1,32 +0,0 @@ | |||
using Microsoft.AspNetCore.Mvc; | |||
namespace Coupon.API.Controllers; | |||
[ApiController] | |||
[Route("[controller]")] | |||
public class WeatherForecastController : ControllerBase | |||
{ | |||
private static readonly string[] Summaries = new[] | |||
{ | |||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" | |||
}; | |||
private readonly ILogger<WeatherForecastController> _logger; | |||
public WeatherForecastController(ILogger<WeatherForecastController> logger) | |||
{ | |||
_logger = logger; | |||
} | |||
[HttpGet(Name = "GetWeatherForecast")] | |||
public IEnumerable<WeatherForecast> Get() | |||
{ | |||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast | |||
{ | |||
Date = DateTime.Now.AddDays(index), | |||
TemperatureC = Random.Shared.Next(-20, 55), | |||
Summary = Summaries[Random.Shared.Next(Summaries.Length)] | |||
}) | |||
.ToArray(); | |||
} | |||
} |
@ -1,13 +0,0 @@ | |||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||
<PropertyGroup> | |||
<TargetFramework>net6.0</TargetFramework> | |||
<Nullable>enable</Nullable> | |||
<ImplicitUsings>enable</ImplicitUsings> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3"/> | |||
</ItemGroup> | |||
</Project> |
@ -1,25 +0,0 @@ | |||
var builder = WebApplication.CreateBuilder(args); | |||
// Add services to the container. | |||
builder.Services.AddControllers(); | |||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle | |||
builder.Services.AddEndpointsApiExplorer(); | |||
builder.Services.AddSwaggerGen(); | |||
var app = builder.Build(); | |||
// Configure the HTTP request pipeline. | |||
if (app.Environment.IsDevelopment()) | |||
{ | |||
app.UseSwagger(); | |||
app.UseSwaggerUI(); | |||
} | |||
app.UseHttpsRedirection(); | |||
app.UseAuthorization(); | |||
app.MapControllers(); | |||
app.Run(); |
@ -1,12 +0,0 @@ | |||
namespace Coupon.API; | |||
public class WeatherForecast | |||
{ | |||
public DateTime Date { get; set; } | |||
public int TemperatureC { get; set; } | |||
public int TemperatureF => 32 + (int) (TemperatureC / 0.5556); | |||
public string? Summary { get; set; } | |||
} |
@ -1,8 +0,0 @@ | |||
{ | |||
"Logging": { | |||
"LogLevel": { | |||
"Default": "Information", | |||
"Microsoft.AspNetCore": "Warning" | |||
} | |||
} | |||
} |
@ -1,9 +0,0 @@ | |||
{ | |||
"Logging": { | |||
"LogLevel": { | |||
"Default": "Information", | |||
"Microsoft.AspNetCore": "Warning" | |||
} | |||
}, | |||
"AllowedHosts": "*" | |||
} |
@ -0,0 +1,20 @@ | |||
using Microsoft.AspNetCore.Mvc; | |||
namespace Coupon.API.Controllers; | |||
[ApiController] | |||
[ApiVersion("1.0")] | |||
[Route("v{version:apiVersion}/[controller]")] | |||
public class CouponController : ControllerBase | |||
{ | |||
[HttpGet("{code}")] | |||
[ProducesResponseType(StatusCodes.Status200OK)] | |||
[ProducesResponseType(StatusCodes.Status400BadRequest)] | |||
[ProducesResponseType(StatusCodes.Status404NotFound)] | |||
public async Task<ActionResult> GetCouponByCodeAsync(string code) | |||
{ | |||
await Task.Delay(500); | |||
return Ok(code); | |||
} | |||
} |
@ -0,0 +1,43 @@ | |||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||
<PropertyGroup> | |||
<TargetFramework>net6.0</TargetFramework> | |||
<Nullable>enable</Nullable> | |||
<ImplicitUsings>enable</ImplicitUsings> | |||
<DebugType>portable</DebugType> | |||
<PreserveCompilationContext>true</PreserveCompilationContext> | |||
<AssemblyName>Coupon.API</AssemblyName> | |||
<PackageId>Coupon.API</PackageId> | |||
<UserSecretsId>aspnet-Coupon.API-20161122013618</UserSecretsId> | |||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||
<GenerateErrorForMissingTargetingPacks>false</GenerateErrorForMissingTargetingPacks> | |||
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="6.0.3" /> | |||
<PackageReference Include="AspNetCore.HealthChecks.MongoDb" Version="6.0.1" /> | |||
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="6.0.2" /> | |||
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="6.0.2" /> | |||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="6.0.4" /> | |||
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" /> | |||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.0.0" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.20.0" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="2.0.2" /> | |||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.3" /> | |||
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.23" /> | |||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.15.0" /> | |||
<PackageReference Include="MongoDB.Driver.Core" Version="2.13.3" /> | |||
<PackageReference Include="Polly" Version="7.2.3" /> | |||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" /> | |||
</ItemGroup> | |||
</Project> |
@ -0,0 +1,14 @@ | |||
namespace Coupon.API; | |||
public class CouponSettings | |||
{ | |||
public string ConnectionString { get; set; } | |||
public string CouponMongoDatabase { get; set; } | |||
public string EventBusConnection { get; set; } | |||
public bool UseCustomizationData { get; set; } | |||
public bool AzureStorageEnabled { get; set; } | |||
} |
@ -0,0 +1,68 @@ | |||
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base | |||
WORKDIR /app | |||
EXPOSE 80 | |||
EXPOSE 443 | |||
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build | |||
WORKDIR /src | |||
# It's important to keep lines from here down to "COPY . ." identical in all Dockerfiles | |||
# to take advantage of Docker's build cache, to speed up local container builds | |||
COPY "eShopOnContainers-ServicesAndWebApps.sln" "eShopOnContainers-ServicesAndWebApps.sln" | |||
COPY "ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj" "ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj" | |||
COPY "ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj" "ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj" | |||
COPY "BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj" "BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj" | |||
COPY "BuildingBlocks/EventBus/EventBus/EventBus.csproj" "BuildingBlocks/EventBus/EventBus/EventBus.csproj" | |||
COPY "BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj" "BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj" | |||
COPY "BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj" "BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj" | |||
COPY "BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj" "BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj" | |||
COPY "BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj" "BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj" | |||
COPY "BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj" "BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj" | |||
COPY "Services/Basket/Basket.API/Basket.API.csproj" "Services/Basket/Basket.API/Basket.API.csproj" | |||
COPY "Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj" "Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj" | |||
COPY "Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj" "Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj" | |||
COPY "Services/Catalog/Catalog.API/Catalog.API.csproj" "Services/Catalog/Catalog.API/Catalog.API.csproj" | |||
COPY "Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj" "Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj" | |||
COPY "Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj" "Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj" | |||
COPY "Services/Coupon/Coupon.API/Coupon.API.csproj" "Services/Coupon/Coupon.API/Coupon.API.csproj" | |||
COPY "Services/Identity/Identity.API/Identity.API.csproj" "Services/Identity/Identity.API/Identity.API.csproj" | |||
COPY "Services/Ordering/Ordering.API/Ordering.API.csproj" "Services/Ordering/Ordering.API/Ordering.API.csproj" | |||
COPY "Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj" "Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj" | |||
COPY "Services/Ordering/Ordering.Domain/Ordering.Domain.csproj" "Services/Ordering/Ordering.Domain/Ordering.Domain.csproj" | |||
COPY "Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj" "Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj" | |||
COPY "Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj" "Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj" | |||
COPY "Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj" "Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj" | |||
COPY "Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj" "Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj" | |||
COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment.API/Payment.API.csproj" | |||
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" | |||
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" | |||
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj" | |||
COPY "Web/WebMVC/WebMVC.csproj" "Web/WebMVC/WebMVC.csproj" | |||
COPY "Web/WebSPA/WebSPA.csproj" "Web/WebSPA/WebSPA.csproj" | |||
COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj" | |||
COPY "docker-compose.dcproj" "docker-compose.dcproj" | |||
COPY "NuGet.config" "NuGet.config" | |||
RUN dotnet restore "eShopOnContainers-ServicesAndWebApps.sln" | |||
COPY . . | |||
WORKDIR /src/Services/Coupon/Coupon.API | |||
RUN dotnet publish --no-restore -c Release -o /app | |||
#FROM build as unittest | |||
#WORKDIR /src/Services/Catalog/Catalog.UnitTests | |||
# | |||
#FROM build as functionaltest | |||
#WORKDIR /src/Services/Catalog/Catalog.FunctionalTests | |||
FROM build AS publish | |||
FROM base AS final | |||
WORKDIR /app | |||
COPY --from=publish /app . | |||
#COPY --from=build /src/Services/Catalog/Catalog.API/Proto /app/Proto | |||
COPY --from=build /src/Services/Coupon/Coupon.API/eshop.pfx . | |||
ENTRYPOINT ["dotnet", "Coupon.API.dll"] |
@ -0,0 +1,22 @@ | |||
FROM mcr.microsoft.com/dotnet/sdk:6.0 | |||
ARG BUILD_CONFIGURATION=Debug | |||
ENV ASPNETCORE_ENVIRONMENT=Development | |||
ENV DOTNET_USE_POLLING_FILE_WATCHER=true | |||
EXPOSE 80 | |||
WORKDIR /src | |||
COPY ["BuildingBlocks/EventBus/EventBus/EventBus.csproj", "BuildingBlocks/EventBus/EventBus/"] | |||
COPY ["BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj", "BuildingBlocks/EventBus/EventBusRabbitMQ/"] | |||
COPY ["BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj", "BuildingBlocks/EventBus/EventBusServiceBus/"] | |||
COPY ["BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj", "BuildingBlocks/EventBus/IntegrationEventLogEF/"] | |||
COPY ["BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj", "BuildingBlocks/WebHostCustomization/WebHost.Customization/"] | |||
COPY ["Services/Catalog/Catalog.API/Catalog.API.csproj", "Services/Catalog/Catalog.API/"] | |||
COPY ["NuGet.config", "NuGet.config"] | |||
RUN dotnet restore Services/Catalog/Catalog.API/Catalog.API.csproj -nowarn:msb3202,nu1503 | |||
COPY . . | |||
WORKDIR "/src/Services/Catalog/Catalog.API" | |||
RUN dotnet build -c $BUILD_CONFIGURATION | |||
ENTRYPOINT ["dotnet", "run", "--no-build", "--no-launch-profile", "-c", "$BUILD_CONFIGURATION", "--"] |
@ -0,0 +1,164 @@ | |||
using System.Data.Common; | |||
using System.Reflection; | |||
using Autofac; | |||
using Coupon.API.Infrastructure.Filters; | |||
using Microsoft.AspNetCore.Mvc; | |||
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.Services; | |||
using Microsoft.Extensions.Diagnostics.HealthChecks; | |||
using Microsoft.Extensions.Options; | |||
using Microsoft.OpenApi.Models; | |||
using RabbitMQ.Client; | |||
namespace Coupon.API.Extensions; | |||
public static class CustomExtensionMethods | |||
{ | |||
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
services.AddApplicationInsightsTelemetry(configuration); | |||
services.AddApplicationInsightsKubernetesEnricher(); | |||
return services; | |||
} | |||
public static IServiceCollection AddCustomMVC(this IServiceCollection services) | |||
{ | |||
services.AddControllers(options => | |||
{ | |||
options.Filters.Add(typeof(HttpGlobalExceptionFilter)); | |||
}) | |||
.AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true); | |||
services.AddCors(options => | |||
{ | |||
options.AddPolicy("CorsPolicy", | |||
builder => builder | |||
.SetIsOriginAllowed((host) => true) | |||
.AllowAnyMethod() | |||
.AllowAnyHeader() | |||
.AllowCredentials()); | |||
}); | |||
return services; | |||
} | |||
public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
var accountName = configuration.GetValue<string>("AzureStorageAccountName"); | |||
var accountKey = configuration.GetValue<string>("AzureStorageAccountKey"); | |||
var hcBuilder = services.AddHealthChecks(); | |||
hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()) | |||
.AddMongoDb( | |||
configuration["ConnectionString"], | |||
name: "CouponCollection-check", | |||
tags: new string[] { "couponcollection" }); | |||
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | |||
{ | |||
hcBuilder | |||
.AddAzureServiceBusTopic( | |||
configuration["EventBusConnection"], | |||
topicName: "eshop_event_bus", | |||
name: "coupon-servicebus-check", | |||
tags: new string[] { "servicebus" }); | |||
} | |||
else | |||
{ | |||
hcBuilder | |||
.AddRabbitMQ( | |||
$"amqp://{configuration["EventBusConnection"]}", | |||
name: "coupon-rabbitmqbus-check", | |||
tags: new string[] { "rabbitmqbus" }); | |||
} | |||
return services; | |||
} | |||
public static IServiceCollection AddCustomOptions(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
services.Configure<CouponSettings>(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 AddSwagger(this IServiceCollection services) | |||
{ | |||
services.AddSwaggerGen(options => | |||
{ | |||
options.SwaggerDoc("v1", new OpenApiInfo | |||
{ | |||
Title = "eShopOnContainers - Coupon HTTP API", | |||
Version = "v1", | |||
Description = "The Coupon Microservice HTTP API. This is a Data-Driven/CRUD microservice sample" | |||
}); | |||
}); | |||
return services; | |||
} | |||
public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
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>(); | |||
string subscriptionName = configuration["SubscriptionClientName"]; | |||
return new EventBusServiceBus(serviceBusPersisterConnection, logger, | |||
eventBusSubcriptionsManager, iLifetimeScope, subscriptionName); | |||
}); | |||
} | |||
else | |||
{ | |||
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp => | |||
{ | |||
var subscriptionClientName = configuration["SubscriptionClientName"]; | |||
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, subscriptionClientName, retryCount); | |||
}); | |||
} | |||
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); | |||
return services; | |||
} | |||
} |
@ -0,0 +1,68 @@ | |||
namespace Coupon.API.Extensions; | |||
public static class WebHostExtensions | |||
{ | |||
public static bool IsInKubernetes(this IWebHost host) | |||
{ | |||
var cfg = host.Services.GetService<IConfiguration>(); | |||
var orchestratorType = cfg.GetValue<string>("OrchestratorType"); | |||
return orchestratorType?.ToUpper() == "K8S"; | |||
} | |||
// public static IWebHost MigrateDbContext<TContext>(this IWebHost host, Action<TContext, IServiceProvider> seeder) where TContext : DbContext | |||
// { | |||
// var underK8s = host.IsInKubernetes(); | |||
// | |||
// using var scope = host.Services.CreateScope(); | |||
// var services = scope.ServiceProvider; | |||
// | |||
// var logger = services.GetRequiredService<ILogger<TContext>>(); | |||
// | |||
// var context = services.GetService<TContext>(); | |||
// | |||
// try | |||
// { | |||
// logger.LogInformation("Migrating database associated with context {DbContextName}", typeof(TContext).Name); | |||
// | |||
// if (underK8s) | |||
// { | |||
// InvokeSeeder(seeder, context, services); | |||
// } | |||
// else | |||
// { | |||
// var retry = Policy.Handle<SqlException>() | |||
// .WaitAndRetry(new TimeSpan[] | |||
// { | |||
// TimeSpan.FromSeconds(3), | |||
// TimeSpan.FromSeconds(5), | |||
// TimeSpan.FromSeconds(8), | |||
// }); | |||
// | |||
// //if the sql server container is not created on run docker compose this | |||
// //migration can't fail for network related exception. The retry options for DbContext only | |||
// //apply to transient exceptions | |||
// // Note that this is NOT applied when running some orchestrators (let the orchestrator to recreate the failing service) | |||
// retry.Execute(() => InvokeSeeder(seeder, context, services)); | |||
// } | |||
// | |||
// logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name); | |||
// } | |||
// catch (Exception ex) | |||
// { | |||
// logger.LogError(ex, "An error occurred while migrating the database used on context {DbContextName}", typeof(TContext).Name); | |||
// if (underK8s) | |||
// { | |||
// throw; // Rethrow under k8s because we rely on k8s to re-run the pod | |||
// } | |||
// } | |||
// | |||
// return host; | |||
// } | |||
// | |||
// private static void InvokeSeeder<TContext>(Action<TContext, IServiceProvider> seeder, TContext context, IServiceProvider services) | |||
// where TContext : DbContext | |||
// { | |||
// context.Database.Migrate(); | |||
// seeder(context, services); | |||
// } | |||
} |
@ -0,0 +1,11 @@ | |||
using Microsoft.AspNetCore.Mvc; | |||
namespace Coupon.API.Infrastructure.ActionResults; | |||
public class InternalServerErrorObjectResult : ObjectResult | |||
{ | |||
public InternalServerErrorObjectResult(object error) : base(error) | |||
{ | |||
StatusCode = StatusCodes.Status500InternalServerError; | |||
} | |||
} |
@ -0,0 +1,64 @@ | |||
using System.Net; | |||
using Coupon.API.Infrastructure.ActionResults; | |||
using Microsoft.AspNetCore.Mvc; | |||
using Microsoft.AspNetCore.Mvc.Filters; | |||
namespace Coupon.API.Infrastructure.Filters; | |||
public class HttpGlobalExceptionFilter : IExceptionFilter | |||
{ | |||
private readonly IWebHostEnvironment env; | |||
private readonly ILogger<HttpGlobalExceptionFilter> logger; | |||
public HttpGlobalExceptionFilter(IWebHostEnvironment 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); | |||
// TODO: Create domain exception | |||
if (context.Exception.GetType() == typeof(Exception)) | |||
{ | |||
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 ocurred." } | |||
}; | |||
if (env.IsDevelopment()) | |||
{ | |||
json.DeveloperMessage = context.Exception; | |||
} | |||
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,53 @@ | |||
using Coupon.API.Extensions; | |||
using HealthChecks.UI.Client; | |||
using Microsoft.AspNetCore.Diagnostics.HealthChecks; | |||
var builder = WebApplication.CreateBuilder(args); | |||
var configuration = builder.Configuration; | |||
builder.Services | |||
.AddEndpointsApiExplorer() | |||
.AddSwagger() | |||
.AddApiVersioning() | |||
.AddAppInsight(configuration) | |||
.AddCustomMVC() | |||
.AddCustomOptions(configuration) | |||
.AddEventBus(configuration) | |||
.AddCustomHealthCheck(configuration); | |||
var app = builder.Build(); | |||
// Configure the HTTP request pipeline. | |||
if (app.Environment.IsDevelopment()) | |||
{ | |||
app.UseSwagger(); | |||
app.UseSwaggerUI(); | |||
} | |||
app.UseHttpsRedirection(); | |||
app.UseRouting(); | |||
app.UseAuthorization(); | |||
var pathBase = configuration["PATH_BASE"]; | |||
if (!string.IsNullOrEmpty(pathBase)) | |||
{ | |||
app.UsePathBase(pathBase); | |||
} | |||
app.UseEndpoints(endpoints => | |||
{ | |||
endpoints.MapControllers(); | |||
endpoints.MapHealthChecks("/hc", new HealthCheckOptions | |||
{ | |||
Predicate = _ => true, | |||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse | |||
}); | |||
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions | |||
{ | |||
Predicate = r => r.Name.Contains("self") | |||
}); | |||
}); | |||
app.Run(); |
@ -0,0 +1,18 @@ | |||
{ | |||
"ConnectionString": "mongodb://localhost:27017", | |||
"CouponMongoDatabase": "CouponDb", | |||
"Serilog": { | |||
"MinimumLevel": { | |||
"Default": "Debug", | |||
"Override": { | |||
"Microsoft": "Warning", | |||
"Microsoft.eShopOnContainers": "Debug", | |||
"System": "Warning" | |||
} | |||
} | |||
}, | |||
"IdentityUrlExternal": "http://localhost:5105", | |||
"IdentityUrl": "http://localhost:5105", | |||
"AzureServiceBusEnabled": false, | |||
"EventBusConnection": "localhost" | |||
} |
@ -0,0 +1,28 @@ | |||
{ | |||
"ConnectionString": null, | |||
"CouponMongoDatabase": "CouponDb", | |||
"UseCustomizationData": false, | |||
"Serilog": { | |||
"SeqServerUrl": null, | |||
"LogstashUrl": null, | |||
"MinimumLevel": { | |||
"Default": "Information", | |||
"Override": { | |||
"Microsoft": "Warning", | |||
"Microsoft.eShopOnContainers": "Information", | |||
"System": "Warning" | |||
} | |||
} | |||
}, | |||
"SubscriptionClientName": "Coupon", | |||
"ApplicationInsights": { | |||
"InstrumentationKey": "" | |||
}, | |||
"EventBusRetryCount": 5, | |||
"UseVault": false, | |||
"Vault": { | |||
"Name": "eshop", | |||
"ClientId": "your-client-id", | |||
"ClientSecret": "your-client-secret" | |||
} | |||
} |