diff --git a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/Kros.CqrsTemplate.nuspec b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/Kros.CqrsTemplate.nuspec index b87e59a..3b8429f 100644 --- a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/Kros.CqrsTemplate.nuspec +++ b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/Kros.CqrsTemplate.nuspec @@ -2,7 +2,7 @@ Kros.Templates.CqrsProject - 1.1.0 + 1.2.0 Create ASP.NET Web project by CQRS pattern. diff --git a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Application/Controllers/RRREntityNameRRR_Plural_Controller.cs b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Application/Controllers/RRREntityNameRRR_Plural_Controller.cs index 70214e7..d385432 100644 --- a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Application/Controllers/RRREntityNameRRR_Plural_Controller.cs +++ b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Application/Controllers/RRREntityNameRRR_Plural_Controller.cs @@ -1,6 +1,8 @@ using Kros.AspNetCore; +using Kros.AspNetCore.Authorization; using Kros.CqrsTemplate.Application.Commands; using Kros.CqrsTemplate.Application.Queries; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; @@ -11,6 +13,7 @@ namespace Kros.CqrsTemplate.Application.Controllers /// /// RRREntityNameRRR_Plural_ controller /// + [Authorize(AuthenticationSchemes = JwtAuthorizationHelper.JwtSchemeName)] [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ValidationProblemDetails))] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] diff --git a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Application/Controllers/SecurityTestController.cs b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Application/Controllers/SecurityTestController.cs index 75954e4..bc11cc8 100644 --- a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Application/Controllers/SecurityTestController.cs +++ b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Application/Controllers/SecurityTestController.cs @@ -1,14 +1,15 @@ -using Microsoft.AspNetCore.Authorization; +using Kros.AspNetCore; +using Kros.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Kros.AspNetCore.Authorization; -using Kros.AspNetCore; namespace Kros.CqrsTemplate.Application.Controllers { /// /// SecurityTest controller. /// + [Authorize(AuthenticationSchemes = JwtAuthorizationHelper.JwtSchemeName)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public class SecurityTestController : ApiBaseController diff --git a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Application/ServiceCollectionExtensions.cs b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Application/ServiceCollectionExtensions.cs index 5a1fdb2..a62d1c0 100644 --- a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Application/ServiceCollectionExtensions.cs +++ b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Application/ServiceCollectionExtensions.cs @@ -4,6 +4,7 @@ using Kros.MediatR.Extensions; using MediatR; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Diagnostics.HealthChecks; using System.Reflection; namespace Microsoft.Extensions.DependencyInjection @@ -13,6 +14,10 @@ namespace Microsoft.Extensions.DependencyInjection /// public static class ServiceCollectionExtensions { + private const string ApiName = "Kros.CqrsTemplate Api"; + private const string ApiVersion = "v1"; + private const string FullApiName = ApiName + " " + ApiVersion; + /// /// Register fluent validation. /// @@ -47,5 +52,17 @@ public static void AddKormDatabase(this IServiceCollection services, IConfigurat public static IServiceCollection AddMediatRDependencies(this IServiceCollection services) => services.AddMediatR(Assembly.GetExecutingAssembly()) .AddMediatRNullCheckPostProcessor(); + + /// + /// Add Health checks. + /// + /// DI container. + /// Configuration. + public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) + => services.AddHealthChecks() + .AddCheck($" {FullApiName}", () => HealthCheckResult.Healthy(), tags: new[] { "api" }) + .AddSqlServer(configuration.GetConnectionString("DefaultConnection"), + name: $" {ApiName} database", + tags: new[] { "db", "sql" }).Services; } } diff --git a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Domain/RRREntityNameRRR_.cs b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Domain/RRREntityNameRRR_.cs index 6e33468..d7e03f4 100644 --- a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Domain/RRREntityNameRRR_.cs +++ b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Domain/RRREntityNameRRR_.cs @@ -1,5 +1,4 @@ -using Kros.KORM.Metadata; -using Kros.KORM.Metadata.Attribute; +using System; namespace Kros.CqrsTemplate.Domain { @@ -12,5 +11,15 @@ public class RRREntityNameRRR_ /// Id. /// public long Id { get; set; } + + /// + /// DateTimeOffset of entity creation. + /// + public DateTimeOffset CreatedTimestamp { get; set; } + + /// + /// DateTimeOffset of last entity update. + /// + public DateTimeOffset LastModifiedTimestamp { get; set; } } } diff --git a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Infrastructure/DatabaseConfiguration.cs b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Infrastructure/DatabaseConfiguration.cs index ec1f512..d8b3d84 100644 --- a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Infrastructure/DatabaseConfiguration.cs +++ b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Infrastructure/DatabaseConfiguration.cs @@ -1,5 +1,6 @@ using Kros.CqrsTemplate.Domain; using Kros.KORM; +using Kros.KORM.Converter; using Kros.KORM.Metadata; namespace Kros.CqrsTemplate.Infrastructure @@ -7,7 +8,7 @@ namespace Kros.CqrsTemplate.Infrastructure /// /// Configure database for KORM. /// - public class DatabaseConfiguration: DatabaseConfigurationBase + public class DatabaseConfiguration : DatabaseConfigurationBase { /// /// Name of RRREntityNameRRR_Plural_ table in database. @@ -22,7 +23,10 @@ public override void OnModelCreating(ModelConfigurationBuilder modelBuilder) { modelBuilder.Entity() .HasTableName(RRREntityNameRRR_Plural_TableName) - .HasPrimaryKey(f => f.Id).AutoIncrement(); + .HasPrimaryKey(f => f.Id).AutoIncrement() + .UseConverterForProperties(NullAndTrimStringConverter.ConvertNull) + .Property(f => f.CreatedTimestamp).UseCurrentTimeValueGenerator(ValueGenerated.OnInsert) + .Property(f => f.LastModifiedTimestamp).UseCurrentTimeValueGenerator(ValueGenerated.OnInsertOrUpdate); } } } diff --git a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Infrastructure/RRREntityNameRRR_Repository.cs b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Infrastructure/RRREntityNameRRR_Repository.cs index 54c4413..0fe694b 100644 --- a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Infrastructure/RRREntityNameRRR_Repository.cs +++ b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Infrastructure/RRREntityNameRRR_Repository.cs @@ -8,7 +8,7 @@ namespace Kros.CqrsTemplate.Infrastructure /// /// Repository for persistating . /// - public class RRREntityNameRRR_Repository: IRRREntityNameRRR_Repository + public class RRREntityNameRRR_Repository : IRRREntityNameRRR_Repository { private IDatabase _database; @@ -23,33 +23,14 @@ public RRREntityNameRRR_Repository(IDatabase database) /// public async Task CreateRRREntityNameRRR_Async(RRREntityNameRRR_ item) - { - var dbSet = _database.Query().AsDbSet(); - - dbSet.Add(item); - - await dbSet.CommitChangesAsync(); - } + => await _database.AddAsync(item); /// public async Task UpdateRRREntityNameRRR_Async(RRREntityNameRRR_ item) - { - var dbSet = _database - .Query() - .AsDbSet(); - - dbSet.Edit(item); - - await dbSet.CommitChangesAsync(); - } + => await _database.EditAsync(item); /// public async Task DeleteRRREntityNameRRR_Async(long id) - { - var dbSet = _database.Query().AsDbSet(); - dbSet.Delete(new RRREntityNameRRR_() { Id = id}); - - await dbSet.CommitChangesAsync(); - } + => await _database.DeleteAsync(id); } } diff --git a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Kros.CqrsTemplate.csproj b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Kros.CqrsTemplate.csproj index 29d13e8..a737002 100644 --- a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Kros.CqrsTemplate.csproj +++ b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Kros.CqrsTemplate.csproj @@ -20,19 +20,22 @@ + + - - - - + + + + - + + diff --git a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Startup.cs b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Startup.cs index 8a55a65..25cb845 100644 --- a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Startup.cs +++ b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/Startup.cs @@ -1,19 +1,22 @@ -using Microsoft.AspNetCore.Builder; +using FluentValidation.AspNetCore; +using Kros.AspNetCore; +using Kros.AspNetCore.Authorization; +using Kros.AspNetCore.HealthChecks; +using Kros.Swagger.Extensions; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.BuilderMiddlewares; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; -using FluentValidation.AspNetCore; -using Microsoft.AspNetCore.BuilderMiddlewares; -using Kros.AspNetCore; using Microsoft.Extensions.Logging; -using Kros.Swagger.Extensions; -using Kros.Identity.Extensions; +using Swashbuckle.AspNetCore.Swagger; namespace Kros.CqrsTemplate { /// /// Startup. /// - public class Startup: BaseStartup + public class Startup : BaseStartup { /// /// Ctor. @@ -31,11 +34,11 @@ public override void ConfigureServices(IServiceCollection services) { base.ConfigureServices(services); - services.AddIdentityServerAuthentication(Configuration); - services.AddWebApi() .AddFluentValidation(); + services.AddApiJwtAuthentication(JwtAuthorizationHelper.JwtSchemeName, Configuration); + services.AddKormDatabase(Configuration); services.AddMediatRDependencies(); @@ -44,7 +47,13 @@ public override void ConfigureServices(IServiceCollection services) .AddClasses() .AsMatchingInterface()); - services.AddSwaggerDocumentation(Configuration); + services + .AddSwaggerDocumentation(Configuration, c => + { + c.AddFluentValidationRules(); + }) + .AddHealthChecks(Configuration) + .AddApplicationInsights(Configuration); } /// @@ -67,6 +76,12 @@ public override void Configure(IApplicationBuilder app, ILoggerFactory loggerFac } app.UseErrorHandling(); + app.UseHealthChecks("/health", new HealthCheckOptions + { + Predicate = _ => true, + ResponseWriter = HealthCheckResponseWriter.WriteHealthCheckResponseAsync + }); + app.UseAuthentication(); app.UseKormMigrations(); app.UseMvc(); diff --git a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/appsettings.Development.json b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/appsettings.Development.json index e203e94..b7e66e1 100644 --- a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/appsettings.Development.json +++ b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/appsettings.Development.json @@ -5,5 +5,14 @@ "System": "Information", "Microsoft": "Information" } + }, + + "ApiJwtAuthorization": { + "JwtSecret": "{secret}", + "RequireHttpsMetadata": true + }, + + "ApplicationInsights": { + "InstrumentationKey": "{instrumentationKey}" } } diff --git a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/appsettings.json b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/appsettings.json index 6f2c6e9..ebb5edb 100644 --- a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/appsettings.json +++ b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/appsettings.json @@ -1,40 +1,44 @@ { - "Logging": { - "LogLevel": { - "Default": "Warning" - } - }, + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, - "AllowedHosts": "*", + "AllowedHosts": "*", "ConnectionStrings": { "DefaultConnection": "" }, - "IdentityServerHandlers": [ - { - "AuthenticationScheme": "", - "ApiName": "", - "AuthorityUrl": "", - "Proxy": { - "Address": "" - }, - "RequireHttpsMetadata": true - } - ], + "KormSettings": { + "DefaultConnection": { + "AutoMigrate": true + } + }, + + "ApiJwtAuthorization": { + "JwtSecret": "", + "RequireHttpsMetadata": true + }, - "SwaggerDocumentation": { - "Version": "v1", - "Title": "API", - "Description": "Web API.", - "Contact": { - "Name": "", - "Email": "", - "Url": "" - }, - "Extensions": { - "TokenUrl": "", - "OAuthClientId": "" - } + "ApplicationInsights": { + "ServiceName": "Kros.CqrsTemplate", + "InstrumentationKey": "" + }, + + "SwaggerDocumentation": { + "Version": "v1", + "Title": "API", + "Description": "Web API.", + "Contact": { + "Name": "", + "Email": "", + "Url": "" + }, + "Extensions": { + "TokenUrl": "", + "OAuthClientId": "" } + } } \ No newline at end of file diff --git a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/appsettings.local.json b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/appsettings.local.json index 93afa42..ca6a4a7 100644 --- a/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/appsettings.local.json +++ b/Kros.ProjectTemplates/src/Kros.CqrsTemplate/content/appsettings.local.json @@ -8,6 +8,6 @@ }, "ConnectionStrings": { - "DefaultConnection": "Server={serverName};Initial Catalog={databaseName};Integrated Security=True;KormAutoMigrate=true;" + "DefaultConnection": "Server={serverName};Initial Catalog={databaseName};Integrated Security=True;" } } \ No newline at end of file