@ -1,11 +0,0 @@ | |||
namespace Webhooks.API.Controllers; | |||
public class HomeController : Controller | |||
{ | |||
// GET: /<controller>/ | |||
public IActionResult Index() | |||
{ | |||
return new RedirectResult("~/swagger"); | |||
} | |||
} |
@ -0,0 +1,47 @@ | |||
internal static class CustomExtensionMethods | |||
{ | |||
public static IServiceCollection AddDbContexts(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
services.AddDbContext<WebhooksContext>(options => | |||
{ | |||
options.UseSqlServer(configuration.GetRequiredConnectionString("WebHooksDb"), | |||
sqlServerOptionsAction: sqlOptions => | |||
{ | |||
sqlOptions.MigrationsAssembly(typeof(Program).Assembly.FullName); | |||
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency | |||
sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); | |||
}); | |||
}); | |||
return services; | |||
} | |||
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
var hcBuilder = services.AddHealthChecks(); | |||
hcBuilder | |||
.AddSqlServer(_ => | |||
configuration.GetRequiredConnectionString("WebHooksDb"), | |||
name: "WebhooksApiDb-check", | |||
tags: new string[] { "ready", "live" }); | |||
return services; | |||
} | |||
public static IServiceCollection AddHttpClientServices(this IServiceCollection services) | |||
{ | |||
// Add http client services | |||
services.AddHttpClient("GrantClient") | |||
.SetHandlerLifetime(TimeSpan.FromMinutes(5)); | |||
return services; | |||
} | |||
public static IServiceCollection AddIntegrationServices(this IServiceCollection services) | |||
{ | |||
return services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>( | |||
sp => (DbConnection c) => new IntegrationEventLogService(c)); | |||
} | |||
} |
@ -1,46 +1,28 @@ | |||
global using HealthChecks.UI.Client; | |||
global using Microsoft.AspNetCore.Authentication.JwtBearer; | |||
global using System; | |||
global using System.Collections.Generic; | |||
global using System.ComponentModel.DataAnnotations; | |||
global using System.Data.Common; | |||
global using System.Linq; | |||
global using System.Net; | |||
global using System.Net.Http; | |||
global using System.Text; | |||
global using System.Text.Json; | |||
global using System.Threading.Tasks; | |||
global using Microsoft.AspNetCore.Authorization; | |||
global using Microsoft.AspNetCore.Builder; | |||
global using Microsoft.AspNetCore.Diagnostics.HealthChecks; | |||
global using Microsoft.AspNetCore.Hosting; | |||
global using Microsoft.AspNetCore.Http; | |||
global using Microsoft.AspNetCore.Mvc.Filters; | |||
global using Microsoft.AspNetCore.Mvc; | |||
global using Microsoft.AspNetCore; | |||
global using Microsoft.EntityFrameworkCore.Design; | |||
global using Microsoft.EntityFrameworkCore; | |||
global using Microsoft.EntityFrameworkCore.Design; | |||
global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||
global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||
global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; | |||
global using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; | |||
global using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; | |||
global using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; | |||
global using Microsoft.Extensions.Configuration; | |||
global using Microsoft.Extensions.DependencyInjection; | |||
global using Microsoft.Extensions.Diagnostics.HealthChecks; | |||
global using Microsoft.Extensions.Hosting; | |||
global using Microsoft.Extensions.Logging; | |||
global using Microsoft.OpenApi.Models; | |||
global using RabbitMQ.Client; | |||
global using Swashbuckle.AspNetCore.SwaggerGen; | |||
global using System.Collections.Generic; | |||
global using System.ComponentModel.DataAnnotations; | |||
global using System.Data.Common; | |||
global using System.IdentityModel.Tokens.Jwt; | |||
global using System.Linq; | |||
global using System.Net.Http; | |||
global using System.Net; | |||
global using System.Reflection; | |||
global using System.Text.Json; | |||
global using System.Text; | |||
global using System.Threading.Tasks; | |||
global using System.Threading; | |||
global using System; | |||
global using Webhooks.API.Exceptions; | |||
global using Webhooks.API.Infrastructure.ActionResult; | |||
global using Services.Common; | |||
global using Webhooks.API.Infrastructure; | |||
global using Webhooks.API.IntegrationEvents; | |||
global using Webhooks.API.Model; | |||
global using Webhooks.API.Services; | |||
global using Webhooks.API; |
@ -1,9 +0,0 @@ | |||
namespace Webhooks.API.Infrastructure.ActionResult; | |||
class InternalServerErrorObjectResult : ObjectResult | |||
{ | |||
public InternalServerErrorObjectResult(object error) : base(error) | |||
{ | |||
StatusCode = StatusCodes.Status500InternalServerError; | |||
} | |||
} |
@ -1,29 +0,0 @@ | |||
namespace Webhooks.API.Infrastructure; | |||
public class AuthorizeCheckOperationFilter : IOperationFilter | |||
{ | |||
public void Apply(OpenApiOperation 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 OpenApiResponse { Description = "Unauthorized" }); | |||
operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" }); | |||
var oAuthScheme = new OpenApiSecurityScheme | |||
{ | |||
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } | |||
}; | |||
operation.Security = new List<OpenApiSecurityRequirement> | |||
{ | |||
new() | |||
{ | |||
[ oAuthScheme ] = new [] { "webhooksapi" } | |||
} | |||
}; | |||
} | |||
} |
@ -1,58 +0,0 @@ | |||
namespace Webhooks.API.Infrastructure; | |||
public class HttpGlobalExceptionFilter : IExceptionFilter | |||
{ | |||
private readonly IWebHostEnvironment _env; | |||
private readonly ILogger<HttpGlobalExceptionFilter> _logger; | |||
public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger<HttpGlobalExceptionFilter> logger) | |||
{ | |||
_env = env; | |||
_logger = logger; | |||
} | |||
public void OnException(ExceptionContext context) | |||
{ | |||
_logger.LogError(new EventId(context.Exception.HResult), | |||
context.Exception, | |||
context.Exception.Message); | |||
if (context.Exception.GetType() == typeof(WebhooksDomainException)) | |||
{ | |||
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[] { context.Exception.Message }); | |||
context.Result = new BadRequestObjectResult(problemDetails); | |||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; | |||
} | |||
else | |||
{ | |||
var json = new JsonErrorResponse | |||
{ | |||
Messages = new[] { "An error occurred." } | |||
}; | |||
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; } | |||
} | |||
} |
@ -1,20 +1,41 @@ | |||
// TODO: Don't do this twice... | |||
var host = CreateWebHostBuilder(args).Build(); | |||
host.Services.MigrateDbContext<WebhooksContext>((_, __) => { }); | |||
host.Run(); | |||
IWebHostBuilder CreateWebHostBuilder(string[] args) => | |||
WebHost.CreateDefaultBuilder(args) | |||
.UseStartup<Startup>() | |||
.ConfigureAppConfiguration((builderContext, config) => | |||
{ | |||
config.AddEnvironmentVariables(); | |||
}) | |||
.ConfigureLogging((hostingContext, builder) => | |||
{ | |||
builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); | |||
builder.AddConsole(); | |||
builder.AddDebug(); | |||
builder.AddAzureWebAppDiagnostics(); | |||
}); | |||
var builder = WebApplication.CreateBuilder(args); | |||
builder.AddServiceDefaults(); | |||
builder.Services.AddControllers(); | |||
builder.Services.AddDbContexts(builder.Configuration); | |||
builder.Services.AddHealthChecks(builder.Configuration); | |||
builder.Services.AddHttpClientServices(); | |||
builder.Services.AddIntegrationServices(); | |||
builder.Services.AddTransient<IIdentityService, IdentityService>(); | |||
builder.Services.AddTransient<IGrantUrlTesterService, GrantUrlTesterService>(); | |||
builder.Services.AddTransient<IWebhooksRetriever, WebhooksRetriever>(); | |||
builder.Services.AddTransient<IWebhooksSender, WebhooksSender>(); | |||
builder.Services.AddTransient<ProductPriceChangedIntegrationEventHandler>(); | |||
builder.Services.AddTransient<OrderStatusChangedToShippedIntegrationEventHandler>(); | |||
builder.Services.AddTransient<OrderStatusChangedToPaidIntegrationEventHandler>(); | |||
var app = builder.Build(); | |||
if (!await app.CheckHealthAsync()) | |||
{ | |||
return; | |||
} | |||
app.UseServiceDefaults(); | |||
app.MapGet("/", () => Results.Redirect("/swagger")); | |||
app.MapControllers(); | |||
var eventBus = app.Services.GetRequiredService<IEventBus>(); | |||
eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>(); | |||
eventBus.Subscribe<OrderStatusChangedToShippedIntegrationEvent, OrderStatusChangedToShippedIntegrationEventHandler>(); | |||
eventBus.Subscribe<OrderStatusChangedToPaidIntegrationEvent, OrderStatusChangedToPaidIntegrationEventHandler>(); | |||
app.Services.MigrateDbContext<WebhooksContext>((_, __) => { }); | |||
await app.RunAsync(); |
@ -1,318 +0,0 @@ | |||
namespace Webhooks.API; | |||
public class Startup | |||
{ | |||
public IConfiguration Configuration { get; } | |||
public Startup(IConfiguration configuration) | |||
{ | |||
Configuration = configuration; | |||
} | |||
public IServiceProvider ConfigureServices(IServiceCollection services) | |||
{ | |||
services | |||
.AddAppInsight(Configuration) | |||
.AddCustomRouting(Configuration) | |||
.AddCustomDbContext(Configuration) | |||
.AddSwagger(Configuration) | |||
.AddCustomHealthCheck(Configuration) | |||
.AddHttpClientServices(Configuration) | |||
.AddIntegrationServices(Configuration) | |||
.AddEventBus(Configuration) | |||
.AddCustomAuthentication(Configuration) | |||
.AddSingleton<IHttpContextAccessor, HttpContextAccessor>() | |||
.AddTransient<IIdentityService, IdentityService>() | |||
.AddTransient<IGrantUrlTesterService, GrantUrlTesterService>() | |||
.AddTransient<IWebhooksRetriever, WebhooksRetriever>() | |||
.AddTransient<IWebhooksSender, WebhooksSender>(); | |||
return services.BuildServiceProvider(); | |||
} | |||
public void Configure(IApplicationBuilder app, ILogger<Startup> logger) | |||
{ | |||
var pathBase = Configuration["PATH_BASE"]; | |||
if (!string.IsNullOrEmpty(pathBase)) | |||
{ | |||
logger.LogDebug("Using PATH BASE '{PathBase}'", pathBase); | |||
app.UsePathBase(pathBase); | |||
} | |||
app.UseRouting(); | |||
app.UseCors("CorsPolicy"); | |||
ConfigureAuth(app); | |||
app.UseEndpoints(endpoints => | |||
{ | |||
endpoints.MapDefaultControllerRoute(); | |||
endpoints.MapControllers(); | |||
endpoints.MapHealthChecks("/hc", new HealthCheckOptions() | |||
{ | |||
Predicate = _ => true, | |||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse | |||
}); | |||
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions | |||
{ | |||
Predicate = r => r.Name.Contains("self") | |||
}); | |||
}); | |||
app.UseSwagger() | |||
.UseSwaggerUI(c => | |||
{ | |||
c.SwaggerEndpoint($"{(!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty)}/swagger/v1/swagger.json", "Webhooks.API V1"); | |||
c.OAuthClientId("webhooksswaggerui"); | |||
c.OAuthAppName("WebHooks Service Swagger UI"); | |||
}); | |||
ConfigureEventBus(app); | |||
} | |||
protected virtual void ConfigureAuth(IApplicationBuilder app) | |||
{ | |||
app.UseAuthentication(); | |||
app.UseAuthorization(); | |||
} | |||
protected virtual void ConfigureEventBus(IApplicationBuilder app) | |||
{ | |||
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>(); | |||
eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>(); | |||
eventBus.Subscribe<OrderStatusChangedToShippedIntegrationEvent, OrderStatusChangedToShippedIntegrationEventHandler>(); | |||
eventBus.Subscribe<OrderStatusChangedToPaidIntegrationEvent, OrderStatusChangedToPaidIntegrationEventHandler>(); | |||
} | |||
} | |||
internal static class CustomExtensionMethods | |||
{ | |||
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
services.AddApplicationInsightsTelemetry(configuration); | |||
services.AddApplicationInsightsKubernetesEnricher(); | |||
return services; | |||
} | |||
public static IServiceCollection AddCustomRouting(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
services.AddControllers(options => | |||
{ | |||
options.Filters.Add(typeof(HttpGlobalExceptionFilter)); | |||
}); | |||
services.AddCors(options => | |||
{ | |||
options.AddPolicy("CorsPolicy", | |||
builder => builder | |||
.SetIsOriginAllowed((host) => true) | |||
.AllowAnyMethod() | |||
.AllowAnyHeader() | |||
.AllowCredentials()); | |||
}); | |||
return services; | |||
} | |||
public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
services.AddEntityFrameworkSqlServer() | |||
.AddDbContext<WebhooksContext>(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: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); | |||
}); | |||
}); | |||
return services; | |||
} | |||
public static IServiceCollection AddSwagger(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
services.AddSwaggerGen(options => | |||
{ | |||
options.SwaggerDoc("v1", new OpenApiInfo | |||
{ | |||
Title = "eShopOnContainers - Webhooks HTTP API", | |||
Version = "v1", | |||
Description = "The Webhooks Microservice HTTP API. This is a simple webhooks CRUD registration entrypoint" | |||
}); | |||
var identityUrl = configuration.GetSection("Identity").GetValue<string>("ExternalUrl"); | |||
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme | |||
{ | |||
Type = SecuritySchemeType.OAuth2, | |||
Flows = new OpenApiOAuthFlows() | |||
{ | |||
Implicit = new OpenApiOAuthFlow() | |||
{ | |||
AuthorizationUrl = new Uri($"{identityUrl}/connect/authorize"), | |||
TokenUrl = new Uri($"{identityUrl}/connect/token"), | |||
Scopes = new Dictionary<string, string>() | |||
{ | |||
{ "webhooks", "Webhooks API" } | |||
} | |||
} | |||
} | |||
}); | |||
options.OperationFilter<AuthorizeCheckOperationFilter>(); | |||
}); | |||
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 logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | |||
var eventBusSubscriptionManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | |||
string subscriptionName = configuration["SubscriptionClientName"]; | |||
return new EventBusServiceBus(serviceBusPersisterConnection, logger, | |||
eventBusSubscriptionManager, sp, subscriptionName); | |||
}); | |||
} | |||
else | |||
{ | |||
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp => | |||
{ | |||
var subscriptionClientName = configuration["SubscriptionClientName"]; | |||
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>(); | |||
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>(); | |||
var eventBusSubscriptionManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | |||
var retryCount = 5; | |||
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) | |||
{ | |||
retryCount = int.Parse(configuration["EventBusRetryCount"]); | |||
} | |||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubscriptionManager, subscriptionClientName, retryCount); | |||
}); | |||
} | |||
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); | |||
services.AddTransient<ProductPriceChangedIntegrationEventHandler>(); | |||
services.AddTransient<OrderStatusChangedToShippedIntegrationEventHandler>(); | |||
services.AddTransient<OrderStatusChangedToPaidIntegrationEventHandler>(); | |||
return services; | |||
} | |||
public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
var hcBuilder = services.AddHealthChecks(); | |||
hcBuilder | |||
.AddCheck("self", () => HealthCheckResult.Healthy()) | |||
.AddSqlServer( | |||
configuration["ConnectionString"], | |||
name: "WebhooksApiDb-check", | |||
tags: new string[] { "webhooksdb" }); | |||
return services; | |||
} | |||
public static IServiceCollection AddHttpClientServices(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | |||
services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(Timeout.InfiniteTimeSpan); | |||
//add http client services | |||
services.AddHttpClient("GrantClient") | |||
.SetHandlerLifetime(TimeSpan.FromMinutes(5)); | |||
return services; | |||
} | |||
public static IServiceCollection AddIntegrationServices(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>( | |||
sp => (DbConnection c) => new IntegrationEventLogService(c)); | |||
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | |||
{ | |||
services.AddSingleton<IServiceBusPersisterConnection>(sp => | |||
{ | |||
var subscriptionClientName = configuration["SubscriptionClientName"]; | |||
return new DefaultServiceBusPersisterConnection(configuration["EventBusConnection"]); | |||
}); | |||
} | |||
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"]; | |||
} | |||
var retryCount = 5; | |||
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) | |||
{ | |||
retryCount = int.Parse(configuration["EventBusRetryCount"]); | |||
} | |||
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); | |||
}); | |||
} | |||
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 = "webhooks"; | |||
options.TokenValidationParameters.ValidateAudience = false; | |||
}); | |||
return services; | |||
} | |||
public static IServiceCollection AddCustomAuthorization(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
services.AddAuthorization(options => | |||
{ | |||
options.AddPolicy("ApiScope", policy => | |||
{ | |||
policy.RequireAuthenticatedUser(); | |||
policy.RequireClaim("scope", "webhooks"); | |||
}); | |||
}); | |||
return services; | |||
} | |||
} |
@ -1,10 +1,44 @@ | |||
{ | |||
"Logging": { | |||
"LogLevel": { | |||
"Default": "Warning" | |||
"Default": "Information", | |||
"Microsoft.AspNetCore": "Warning" | |||
} | |||
}, | |||
"AllowedHosts": "*", | |||
"SubscriptionClientName": "Webhooks", | |||
"EventBusRetryCount": 5 | |||
"OpenApi": { | |||
"Endpoint": { | |||
"Name": "Webhooks.API V1" | |||
}, | |||
"Document": { | |||
"Description": "The Webhooks Microservice HTTP API. This is a simple webhooks CRUD registration entrypoint", | |||
"Title": "eShopOnContainers - Webhooks HTTP API", | |||
"Version": "v1" | |||
}, | |||
"Auth": { | |||
"ClientId": "webhooksswaggerui", | |||
"AppName": "WebHooks Service Swagger UI" | |||
} | |||
}, | |||
"ConnectionStrings": { | |||
"EventBus": "localhost" | |||
}, | |||
"EventBus": { | |||
"SubscriptionClientName": "Webhooks", | |||
"RetryCount": 5 | |||
}, | |||
"ApplicationInsights": { | |||
"InstrumentationKey": "" | |||
}, | |||
"Identity": { | |||
"Url": "http://localhost:5105", | |||
"ExternalUrl": "http://localhost:5105", | |||
"Audience": "webhooks", | |||
"Scopes": { | |||
"webhooks": "Webhooks API" | |||
} | |||
}, | |||
"UseCustomizationData": false, | |||
"GracePeriodTime": "1", | |||
"CheckUpdateTime": "30000" | |||
} |