Skip to content

Commit

Permalink
Merge pull request #7 from sj-distributor/clutter
Browse files Browse the repository at this point in the history
Enhance infrastructure
  • Loading branch information
linwenda authored Jan 31, 2023
2 parents 870c1c6 + 6071368 commit 258e58b
Show file tree
Hide file tree
Showing 22 changed files with 303 additions and 131 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,20 @@
# Wax

[![.NET](https://github.com/sj-distributor/Wax/actions/workflows/dotnet.yml/badge.svg)](https://github.com/sj-distributor/Wax/actions/workflows/dotnet.yml)
[![Nuget](https://img.shields.io/nuget/v/Wax.Template)](https://www.nuget.org/packages/Wax.Template)

WilTechs Architecture Solution Template for .NET 6

## Getting Started

Using dotnet cli template, install the template:

```
dotnet new -i Wax.Template
```

Run this command to create the solution:

```
dotnet new wax -o ProjectName
```
43 changes: 20 additions & 23 deletions src/Wax.Api/Filters/GlobalExceptionFilter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Wax.Core.Exceptions;

Expand Down Expand Up @@ -39,28 +38,12 @@ private void HandleBusinessException(ExceptionContext context)
{
_logger.Warning(context.Exception.Message);

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("BusinessValidations", new string[] { context.Exception.Message });

context.Result = new BadRequestObjectResult(problemDetails);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
}

private void HandleInternalServerError(ExceptionContext context)
{
_logger.Error(context.Exception, context.Exception.Message);

var problemDetails = new ProblemDetails
{
Status = StatusCodes.Status500InternalServerError,
Title = "Internal error",
Detail = "An error occur.Try it again."
Status = StatusCodes.Status403Forbidden,
Title = "Business error",
Detail = context.Exception.Message,
Instance = context.HttpContext.Request.Path
};

context.Result = new ObjectResult(problemDetails);
Expand All @@ -76,7 +59,7 @@ private void HandleEntityNotFoundException(ExceptionContext context)
{
Status = StatusCodes.Status404NotFound,
Title = "The specified resource was not found.",
Detail = exception.Message
Detail = exception.Message,
};

context.Result = new NotFoundObjectResult(details);
Expand All @@ -93,4 +76,18 @@ private void HandleValidationException(ExceptionContext context)

context.ExceptionHandled = true;
}

private void HandleInternalServerError(ExceptionContext context)
{
_logger.Error(context.Exception, context.Exception.Message);

var problemDetails = new ProblemDetails
{
Status = StatusCodes.Status500InternalServerError,
Title = "Internal error",
Detail = "An error occur.Try it again later."
};

context.Result = new ObjectResult(problemDetails);
}
}
50 changes: 45 additions & 5 deletions src/Wax.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,27 @@

namespace Wax.Api;

public class Program
public partial class Program
{
public static void Main(string[] args)
{
var application = typeof(Program).Namespace ?? "Wax.Api";

var configuration = GetConfiguration();

Log.Logger = CreateSerilogLogger(configuration);

try
{
Log.Information("Configuring api host ({ApplicationContext})... ", AppName);

var webHost = CreateWebHostBuilder(args).Build();

Log.Information("Starting api host ({ApplicationContext})...", AppName);

webHost.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", application);
Log.Fatal(ex, "Program terminated unexpectedly!");
}
finally
{
Expand All @@ -27,6 +33,40 @@ public static void Main(string[] args)

private static IHostBuilder CreateWebHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(l => l.AddSerilog(Log.Logger))
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(builder => { builder.UseStartup<Startup>(); });
.ConfigureWebHostDefaults(builder => { builder.UseStartup<Startup>(); }).UseSerilog();

private static IConfiguration GetConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true)
.AddEnvironmentVariables();

return builder.Build();
}

private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
{
var seqServerUrl = configuration["Serilog:Seq:ServerUrl"];
var seqApiKey = configuration["Serilog:Seq:ApiKey"];

return new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.WithProperty("ApplicationContext", AppName)
.Enrich.FromLogContext()
.Enrich.WithCorrelationId()
.WriteTo.Console()
.WriteTo.Seq(seqServerUrl, apiKey: seqApiKey)
.CreateLogger();
}
}

public partial class Program
{
private static readonly string Namespace = typeof(Startup).Namespace;

private static readonly string AppName =
Namespace[(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1)..];
}
16 changes: 3 additions & 13 deletions src/Wax.Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,18 @@ public class Startup
public Startup(IConfiguration configuration)
{
Configuration = configuration;

Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.CreateLogger();

Log.Information("Starting up");
}

// ConfigureServices is where you register dependencies. This gets
// called by the runtime before the ConfigureContainer method, below.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
options.Filters.Add<GlobalExceptionFilter>();
});
services.AddControllers(options => { options.Filters.Add<GlobalExceptionFilter>(); });
services.AddOptions();
services.AddCustomSwagger();
services.AddScoped<ICurrentUser, CurrentUser>();
services.AddLogging();
services.AddHttpContextAccessor();

_serviceCollection = services;
}
Expand All @@ -62,10 +54,8 @@ public void ConfigureContainer(ContainerBuilder builder)
// Configure is where you add middleware. This is called after
// ConfigureContainer. You can use IApplicationBuilder.ApplicationServices
// here if you need to resolve things from the container.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
loggerFactory.AddSerilog();

if (env.IsDevelopment())
{
app.UseSwagger();
Expand Down
4 changes: 2 additions & 2 deletions src/Wax.Api/Wax.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Serilog.AspNetCore" Version="6.1.0" />
<PackageReference Include="Serilog.Enrichers.CorrelationId" Version="3.0.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.Seq" Version="5.2.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.4.0" />
</ItemGroup>
Expand Down
32 changes: 3 additions & 29 deletions src/Wax.Api/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,35 +1,9 @@
{
"AllowedHosts": "*",
"Serilog": {
"Using": [
"Serilog.Sinks.Console"
],
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "Seq",
"Args": {
"serverUrl": "http://localhost:5341"
}
}
],
"Enrich": [
"FromLogContext",
"WithMachineName",
"WithThreadId",
"WithCorrelationId"
],
"Properties": {
"Application": "Wax.Api"
"Seq": {
"ServerUrl": "http://localhost:5341",
"ApiKey": ""
}
},
"ConnectionStrings": {
Expand Down
28 changes: 4 additions & 24 deletions src/Wax.Core/ApplicationModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
using AutoMapper.Contrib.Autofac.DependencyInjection;
using Mediator.Net;
using Mediator.Net.Autofac;
using Mediator.Net.Middlewares.Serilog;
using Microsoft.EntityFrameworkCore;
using Serilog;
using Wax.Core.Data;
using Wax.Core.Data.Repositories;
using Wax.Core.DependencyInjection;
using Wax.Core.Domain;
using Wax.Core.Domain.Customers;
using Wax.Core.Processing.FluentMessageValidator;
using Wax.Core.Middlewares.FluentMessageValidator;
using Wax.Core.Middlewares.Logging;
using Wax.Core.Services.Identity;
using Module = Autofac.Module;

Expand All @@ -32,7 +31,7 @@ public ApplicationModule(ILogger logger, ICurrentUser currentUser, string connec
_connectionString = connectionString;

_assemblies = (assemblies ?? Array.Empty<Assembly>())
.Concat(new[] {typeof(ApplicationModule).Assembly})
.Concat(new[] { typeof(ApplicationModule).Assembly })
.ToArray();
}

Expand Down Expand Up @@ -88,9 +87,6 @@ private void RegisterDatabase(ContainerBuilder builder)
builder.RegisterGeneric(typeof(EfCoreRepository<>))
.As(typeof(IRepository<>))
.InstancePerLifetimeScope();

//TODO: Register all repository
builder.RegisterType<EfCoreCustomerRepository>().As<ICustomerRepository>().InstancePerLifetimeScope();
}

private void RegisterIdentity(ContainerBuilder builder)
Expand All @@ -112,7 +108,7 @@ private void RegisterMediator(ContainerBuilder builder)
mediatorBuilder.RegisterHandlers(_assemblies);
mediatorBuilder.ConfigureGlobalReceivePipe(c =>
{
c.UseSerilog(logger: _logger);
c.UseLogger();
c.UseMessageValidator();
});

Expand All @@ -125,21 +121,5 @@ private void RegisterValidator(ContainerBuilder builder)
.Where(t => t.IsClass && typeof(IFluentMessageValidator).IsAssignableFrom(t))
.AsSelf().AsImplementedInterfaces();
}

private static bool IsAssignableToGenericType(Type givenType, Type genericType)
{
var interfaceTypes = givenType.GetInterfaces();

if (interfaceTypes.Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == genericType))
{
return true;
}

if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
return true;

var baseType = givenType.BaseType;
return baseType != null && IsAssignableToGenericType(baseType, genericType);
}
}
}
3 changes: 2 additions & 1 deletion src/Wax.Core/Data/Repositories/EfCoreCustomerRepository.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using Microsoft.EntityFrameworkCore;
using Wax.Core.DependencyInjection;
using Wax.Core.Domain.Customers;

namespace Wax.Core.Data.Repositories;

public class EfCoreCustomerRepository : EfCoreRepository<Customer>, ICustomerRepository
public class EfCoreCustomerRepository : EfCoreRepository<Customer>, ICustomerRepository, IScopedDependency
{
private readonly DbSet<Customer> _customers;

Expand Down
6 changes: 1 addition & 5 deletions src/Wax.Core/Exceptions/BusinessException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@
{
public class BusinessException : Exception
{
public BusinessException()
{
}

public BusinessException(string message) : base(message)
protected BusinessException(string message) : base(message)
{
}
}
Expand Down
42 changes: 42 additions & 0 deletions src/Wax.Core/Extensions/TypeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
namespace Wax.Core.Extensions;

public static class TypeExtensions
{
public static bool IsAssignableToGenericType(this Type givenType, Type genericType)
{
var interfaceTypes = givenType.GetInterfaces();

if (interfaceTypes.Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == genericType))
{
return true;
}

if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
return true;

var baseType = givenType.BaseType;
return baseType != null && IsAssignableToGenericType(baseType, genericType);
}

public static string GetGenericTypeName(this Type type)
{
string typeName;

if (type.IsGenericType)
{
var genericTypes = string.Join(",", type.GetGenericArguments().Select(t => t.Name).ToArray());
typeName = $"{type.Name.Remove(type.Name.IndexOf('`'))}<{genericTypes}>";
}
else
{
typeName = type.Name;
}

return typeName;
}

public static string GetGenericTypeName(this object @object)
{
return @object.GetType().GetGenericTypeName();
}
}
Loading

0 comments on commit 258e58b

Please sign in to comment.