├── README.md
├── .gitmodules
├── src
├── BuildingBlocks.Core
│ ├── BuildingBlocks.Core.csproj
│ ├── IUser.cs
│ └── IApiInfo.cs
├── BuildingBlocks.Idempotency
│ ├── BuildingBlocks.Idempotency.csproj
│ ├── IRequestManager.cs
│ ├── InMemoryRequestManager.cs
│ └── CircularQueue.cs
├── BuildingBlocks.Mediatr
│ ├── Commands
│ │ ├── ICommand.cs
│ │ ├── IdentifiedCommand.cs
│ │ └── IdentifiedCommandHandler.cs
│ ├── Exceptions
│ │ ├── MediatrPipelineException.cs
│ │ ├── ThrowMediatrPipelineException.cs
│ │ └── HttpGlobalExceptionFilter.cs
│ ├── BuildingBlocks.Mediatr.csproj
│ ├── Validators
│ │ └── ValidatorsBehavior.cs
│ └── Autofac
│ │ └── MediatrModule.cs
├── BuildingBlocks.AspnetCoreIdentity.RavenDB
│ ├── Role.cs
│ ├── BuildingBlocks.AspnetCoreIdentity.RavenDB.csproj
│ ├── Startup.cs
│ ├── User.cs
│ ├── RoleStore.cs
│ └── UserStore.cs
├── BuildingBlocks.Autofac
│ ├── BuildingBlocks.Autofac.csproj
│ └── Startup.cs
├── BuindingBlocks.Resilience
│ ├── BuindingBlocks.Resilience.csproj
│ └── Http
│ │ ├── Authorization.cs
│ │ ├── IHttpClient.cs
│ │ ├── HttpRequestMessageExtensions.cs
│ │ ├── ResilientHttpClient.cs
│ │ └── StandardHttpClient.cs
├── BuildingBlocks.IdentityServer4.RavenDB
│ ├── BuildingBlocks.IdentityServer4.RavenDB.csproj
│ ├── Extensions
│ │ └── Startup.cs
│ └── Stores
│ │ ├── ClientStore.cs
│ │ ├── PersistedGrantStore.cs
│ │ └── ResourceStore.cs
├── BuildingBlocks.Mvc
│ ├── BuildingBlocks.Mvc.csproj
│ ├── SetupFeatureFolders.cs
│ ├── ModelStateExtensions.cs
│ ├── FeaturesLocationExpander.cs
│ ├── HttpContextUser.cs
│ ├── SetupPermissiveCors.cs
│ └── SetupIdentity.cs
└── BuildingBlocks.Swagger
│ ├── BuildingBlocks.Swagger.csproj
│ ├── OperationFilterContextExtensions.cs
│ ├── AuthorizeCheckOperationFilter.cs
│ └── Startup.cs
├── .gitignore
└── BuildingBlocks.sln
/README.md:
--------------------------------------------------------------------------------
1 | # BuildingBlocks
2 | Building blocks for Aspnet Core Microservices Development.
3 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "submodules/HealthChecks"]
2 | path = submodules/HealthChecks
3 | url = https://github.com/seven1986/HealthChecks.git
4 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Core/BuildingBlocks.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Idempotency/BuildingBlocks.Idempotency.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mediatr/Commands/ICommand.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace BuildingBlocks.Mediatr.Commands
4 | {
5 | public interface ICommand : IRequest
6 | {
7 | }
8 |
9 | public interface ICommand : ICommand
10 | {
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.AspnetCoreIdentity.RavenDB/Role.cs:
--------------------------------------------------------------------------------
1 | namespace BuildingBlocks.AspnetCoreIdentity.RavenDB
2 | {
3 | public class Role
4 | {
5 | public string Id { get; set; }
6 | public string Name { get; set; }
7 | public string NormalizedName { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Core/IUser.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Security.Claims;
3 |
4 | namespace BuildingBlocks.Core
5 | {
6 | public interface IUser
7 | {
8 | string Id { get; }
9 | string Name { get; }
10 | IEnumerable Claims { get; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Autofac/BuildingBlocks.Autofac.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mediatr/Exceptions/MediatrPipelineException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace BuildingBlocks.Mediatr.Validators
4 | {
5 | public class MediatrPipelineException : Exception
6 | {
7 | public MediatrPipelineException(string message, Exception innerException)
8 | : base(message, innerException)
9 | {
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/src/BuindingBlocks.Resilience/BuindingBlocks.Resilience.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.IdentityServer4.RavenDB/BuildingBlocks.IdentityServer4.RavenDB.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Idempotency/IRequestManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace BuildingBlocks.Idempotency
6 | {
7 | public interface IRequestManager
8 | {
9 | Task IsRegistered(
10 | Guid id,
11 | CancellationToken cancellationToken = default(CancellationToken));
12 |
13 | Task Register(Guid id,
14 | CancellationToken cancellationToken = default(CancellationToken));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mediatr/Commands/IdentifiedCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using MediatR;
3 |
4 | namespace BuildingBlocks.Mediatr.Commands
5 | {
6 | public class IdentifiedCommand :
7 | IRequest
8 | where TCommand : IRequest
9 | {
10 | public Guid Id { get; }
11 | public TCommand Command { get; }
12 |
13 | public IdentifiedCommand(TCommand command, Guid id)
14 | {
15 | Id = id;
16 | Command = command;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mvc/BuildingBlocks.Mvc.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Swagger/BuildingBlocks.Swagger.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.AspnetCoreIdentity.RavenDB/BuildingBlocks.AspnetCoreIdentity.RavenDB.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Swagger/OperationFilterContextExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Microsoft.AspNetCore.Authorization;
3 | using Swashbuckle.AspNetCore.SwaggerGen;
4 |
5 | namespace BuildingBlocks.Swagger
6 | {
7 | internal static class OperationFilterContextExtensions
8 | {
9 | internal static bool HasAuthorize(this OperationFilterContext context)
10 | {
11 | var apiDescription = context.ApiDescription;
12 |
13 | return
14 | apiDescription.ControllerAttributes().OfType().Any() ||
15 | apiDescription.ActionAttributes().OfType().Any();
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mvc/SetupFeatureFolders.cs:
--------------------------------------------------------------------------------
1 | // ReSharper disable once CheckNamespace
2 |
3 | using BuildingBlocks.Mvc;
4 | using Microsoft.AspNetCore.Mvc.Razor;
5 |
6 | namespace Microsoft.Extensions.DependencyInjection
7 | {
8 | public static class SetupFeatureFolders
9 | {
10 | public static IServiceCollection AddFeatureFoldersSupport(
11 | this IServiceCollection services
12 | )
13 | {
14 | services.Configure(options =>
15 | {
16 | options.ViewLocationExpanders.Add(new FeaturesLocationExpander());
17 | });
18 | return services;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mediatr/BuildingBlocks.Mediatr.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mvc/ModelStateExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Microsoft.AspNetCore.Mvc;
3 | using Microsoft.AspNetCore.Mvc.ModelBinding;
4 |
5 | namespace BuildingBlocks.Mvc
6 | {
7 | public static class ModelStateExtensions
8 | {
9 | public static BadRequestObjectResult GenerateBadRequestFromExceptions(
10 | this ModelStateDictionary modelState
11 | )
12 | {
13 | var errors = modelState.Values
14 | .SelectMany(value => value.Errors)
15 | .Select(error => error.Exception.InnerException.Message)
16 | .ToArray();
17 |
18 | return new BadRequestObjectResult(errors);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/BuindingBlocks.Resilience/Http/Authorization.cs:
--------------------------------------------------------------------------------
1 | namespace BuindingBlocks.Resilience.Http
2 | {
3 | public struct Authorization
4 | {
5 | public string Token { get; }
6 | public string Method { get; }
7 |
8 | public Authorization(
9 | string token = null,
10 | string method = "Bearer"
11 | )
12 | {
13 | Token = token;
14 | Method = method;
15 | }
16 |
17 | public static readonly Authorization Empty = new Authorization();
18 |
19 | public static implicit operator Authorization(string token) =>
20 | new Authorization(token);
21 |
22 | public bool IsEmpty => Token == null;
23 | }
24 | }
--------------------------------------------------------------------------------
/src/BuildingBlocks.Idempotency/InMemoryRequestManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace BuildingBlocks.Idempotency
6 | {
7 | public class InMemoryRequestManager : IRequestManager
8 | {
9 | private readonly CircularQueue _queue = new CircularQueue();
10 |
11 | public Task IsRegistered(Guid id, CancellationToken cancellationToken = default(CancellationToken)) =>
12 | Task.FromResult(_queue.Contains(id));
13 |
14 | public Task Register(Guid id, CancellationToken cancellationToken = default(CancellationToken))
15 | {
16 | _queue.Enqueue(id);
17 | return Task.CompletedTask;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mvc/FeaturesLocationExpander.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Microsoft.AspNetCore.Mvc.Razor;
3 |
4 | namespace BuildingBlocks.Mvc
5 | {
6 | public class FeaturesLocationExpander : IViewLocationExpander
7 |
8 | {
9 | public void PopulateValues(ViewLocationExpanderContext context)
10 | {
11 | // nothing
12 | }
13 |
14 | public IEnumerable ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable viewLocations)
15 | {
16 | return new[]
17 | {
18 | "/Features/{1}/{0}.cshtml", // feature specific content
19 | "/Features/Shared/{0}.cshtml" // shared
20 | };
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Autofac/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Autofac;
3 | using Autofac.Core;
4 | using Autofac.Extensions.DependencyInjection;
5 |
6 | namespace Microsoft.Extensions.DependencyInjection
7 | {
8 | public static class Startup
9 | {
10 | public static IServiceProvider ConvertToAutofac(
11 | this IServiceCollection services,
12 | params IModule[] modules
13 | )
14 | {
15 | var container = new ContainerBuilder();
16 | foreach (var module in modules)
17 | {
18 | container.RegisterModule(module);
19 | }
20 | container.Populate(services);
21 | return new AutofacServiceProvider(container.Build());
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/BuindingBlocks.Resilience/Http/IHttpClient.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace BuindingBlocks.Resilience.Http
6 | {
7 | public interface IHttpClient
8 | {
9 | Task GetStringAsync(
10 | string uri,
11 | Authorization authorization = default(Authorization),
12 | CancellationToken cancellationToken = default(CancellationToken)
13 | );
14 |
15 | Task PutAsync(
16 | string uri, T item,
17 | string requestId = null,
18 | Authorization authorization = default(Authorization),
19 | CancellationToken cancellationToken = default(CancellationToken)
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mvc/HttpContextUser.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Security.Claims;
4 | using BuildingBlocks.Core;
5 | using Microsoft.AspNetCore.Http;
6 |
7 | namespace BuildingBlocks.Mvc
8 | {
9 | public class HttpContextUser : IUser
10 | {
11 | private readonly IHttpContextAccessor _httpContextAccessor;
12 |
13 | public HttpContextUser(IHttpContextAccessor httpContextAccessor)
14 | {
15 | _httpContextAccessor = httpContextAccessor;
16 | }
17 |
18 | public string Id => _httpContextAccessor.HttpContext.User.Claims.First(c => c.Type == "sub").Value;
19 | public string Name => _httpContextAccessor.HttpContext.User.Identity.Name;
20 | public IEnumerable Claims => _httpContextAccessor.HttpContext.User.Claims;
21 | }
22 | }
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mvc/SetupPermissiveCors.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 |
3 | namespace Microsoft.Extensions.DependencyInjection
4 | {
5 | public static class SetupPermissiveCors
6 | {
7 | public static IServiceCollection AddPermissiveCors(
8 | this IServiceCollection services
9 | ) => services
10 | .AddCors(options =>
11 | {
12 | options.AddPolicy("PermissiveCorsPolicy", builder => builder
13 | .AllowAnyOrigin()
14 | .AllowAnyMethod()
15 | .AllowAnyHeader()
16 | .AllowCredentials()
17 | );
18 | });
19 |
20 | public static IApplicationBuilder UsePermissiveCors(this IApplicationBuilder app)
21 | => app.UseCors("PermissiveCorsPolicy");
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Idempotency/CircularQueue.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Concurrent;
2 | using System.Linq;
3 |
4 | namespace BuildingBlocks.Idempotency
5 | {
6 | public class CircularQueue
7 | {
8 | private readonly ConcurrentQueue _innerQueue = new ConcurrentQueue();
9 | private readonly object _lockObject = new object();
10 |
11 | public int Limit { get; }
12 |
13 | public CircularQueue(int limit = 1000)
14 | {
15 | Limit = limit;
16 | }
17 |
18 | public void Enqueue(T obj)
19 | {
20 | lock (_lockObject)
21 | {
22 | _innerQueue.Enqueue(obj);
23 | while (_innerQueue.Count > Limit && _innerQueue.TryDequeue(out T _)) ;
24 | }
25 | }
26 |
27 | public bool Contains(T value)
28 | {
29 | lock (_lockObject)
30 | {
31 | return _innerQueue.Contains(value);
32 | }
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/src/BuildingBlocks.Core/IApiInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Reflection;
3 |
4 | namespace BuildingBlocks.Core
5 | {
6 | public interface IApiInfo
7 | {
8 | string AuthenticationAuthority { get; }
9 | string JwtBearerAudience { get; }
10 | string Code { get; }
11 | string Title { get; }
12 | string Version { get; }
13 | Assembly ApplicationAssembly { get; }
14 |
15 | IDictionary Scopes { get; }
16 | SwaggerAuthInfo SwaggerAuthInfo { get; }
17 |
18 | }
19 |
20 | public class SwaggerAuthInfo
21 | {
22 | public string ClientId { get; }
23 | public string Secret { get; }
24 | public string Realm { get; }
25 |
26 | public SwaggerAuthInfo(
27 | string clientId,
28 | string secret,
29 | string realm
30 | )
31 | {
32 | ClientId = clientId;
33 | Secret = secret;
34 | Realm = realm;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.AspnetCoreIdentity.RavenDB/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Identity;
3 | using BuildingBlocks.AspnetCoreIdentity.RavenDB;
4 | using Raven.Client.Documents;
5 |
6 | // ReSharper disable once CheckNamespace
7 | namespace Microsoft.Extensions.DependencyInjection
8 | {
9 | public static class Startup
10 | {
11 | public static IServiceCollection AddRavenDBIdentity(
12 | this IServiceCollection services,
13 | IDocumentStore documentStore, string databaseName
14 | )
15 | {
16 | var session = documentStore.OpenAsyncSession(databaseName);
17 | services.AddIdentity();
18 |
19 | services.AddScoped>(
20 | (sp) => new UserStore(session)
21 | );
22 | services.AddScoped>(
23 | (sp) => new RoleStore(session)
24 | );
25 |
26 | return services;
27 | }
28 |
29 | public static IApplicationBuilder UseRavenDBIdentity(this IApplicationBuilder app) => app;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Swagger/AuthorizeCheckOperationFilter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using BuildingBlocks.Core;
4 | using Swashbuckle.AspNetCore.Swagger;
5 | using Swashbuckle.AspNetCore.SwaggerGen;
6 |
7 | namespace BuildingBlocks.Swagger
8 | {
9 | public class AuthorizeCheckOperationFilter : IOperationFilter
10 | {
11 | private readonly IApiInfo _apiInfo;
12 |
13 | public AuthorizeCheckOperationFilter(IApiInfo apiInfo)
14 | {
15 | _apiInfo = apiInfo;
16 | }
17 |
18 | public void Apply(
19 | Operation operation,
20 | OperationFilterContext context
21 | )
22 | {
23 | if (!context.HasAuthorize()) return;
24 |
25 | operation.Responses.Add("401", new Response { Description = "Unauthorized" });
26 | operation.Responses.Add("403", new Response { Description = "Forbidden" });
27 |
28 | operation.Security = new List>>
29 | {
30 | new Dictionary>
31 | {
32 | {"oauth2", _apiInfo.Scopes.Keys.ToArray() }
33 | }
34 | };
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/BuindingBlocks.Resilience/Http/HttpRequestMessageExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Net.Http.Headers;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace BuindingBlocks.Resilience.Http
6 | {
7 | public static class HttpRequestMessageExtensions
8 | {
9 | public static HttpRequestMessage CopyAuthorizationHeaderFrom(
10 | this HttpRequestMessage request,
11 | HttpContext context
12 | )
13 | {
14 | var authorizationHeader = context
15 | .Request
16 | .Headers["Authorization"];
17 |
18 | if (!string.IsNullOrEmpty(authorizationHeader))
19 | {
20 | request.Headers.Add("Authorization", new string[] { authorizationHeader });
21 | }
22 |
23 | return request;
24 | }
25 |
26 | public static HttpRequestMessage Apply(
27 | this HttpRequestMessage request,
28 | Authorization authorization
29 | )
30 | {
31 | if (!authorization.IsEmpty)
32 | {
33 | request.Headers.Authorization = new AuthenticationHeaderValue(
34 | authorization.Method,
35 | authorization.Token
36 | );
37 | }
38 | return request;
39 | }
40 |
41 | }
42 | }
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mvc/SetupIdentity.cs:
--------------------------------------------------------------------------------
1 | using System.IdentityModel.Tokens.Jwt;
2 | using BuildingBlocks.Core;
3 | using BuildingBlocks.Mvc;
4 | using Microsoft.AspNetCore.Authentication.JwtBearer;
5 | using Microsoft.AspNetCore.Http;
6 |
7 | // ReSharper disable once CheckNamespace
8 | namespace Microsoft.Extensions.DependencyInjection
9 | {
10 | public static class SetupIdentity
11 | {
12 | public static IServiceCollection AddCustomIdentity(
13 | this IServiceCollection services,
14 | IApiInfo apiInfo
15 | )
16 | {
17 | JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
18 |
19 | services.AddAuthentication(options =>
20 | {
21 | options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
22 | options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
23 |
24 | }).AddJwtBearer(options =>
25 | {
26 | options.Authority = apiInfo.AuthenticationAuthority;
27 | options.RequireHttpsMetadata = false;
28 | options.Audience = apiInfo.JwtBearerAudience;
29 | });
30 |
31 | services.AddSingleton();
32 | services.AddScoped();
33 |
34 | return services;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mediatr/Validators/ValidatorsBehavior.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using FluentValidation;
5 | using MediatR;
6 |
7 | namespace BuildingBlocks.Mediatr.Validators
8 | {
9 | public class ValidatorsBehavior
10 | : IPipelineBehavior
11 | {
12 | private readonly IValidator[] _validators;
13 |
14 | public ValidatorsBehavior(IValidator[] validators)
15 | {
16 | _validators = validators;
17 | }
18 |
19 | public async Task Handle(
20 | TRequest request,
21 | CancellationToken cancellationToken,
22 | RequestHandlerDelegate next)
23 | {
24 | var failures = _validators
25 | .Select(validator => validator.Validate(request))
26 | .SelectMany(result => result.Errors)
27 | .Where(error => error != null)
28 | .ToList();
29 |
30 | if (failures.Any())
31 | {
32 | throw new MediatrPipelineException(
33 | $"Command Validation Errors for type {typeof(TRequest).Name}",
34 | new ValidationException("Validation exception", failures)
35 | );
36 | }
37 |
38 | return await next();
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.IdentityServer4.RavenDB/Extensions/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using BuildingBlocks.IdentityServer4.RavenDB.Stores;
3 | using IdentityServer4.Stores;
4 | using Raven.Client.Documents;
5 |
6 | // ReSharper disable once CheckNamespace
7 | namespace Microsoft.Extensions.DependencyInjection
8 | {
9 | // ReSharper disable once InconsistentNaming
10 | public static class Startup
11 | {
12 | public static IIdentityServerBuilder AddRavenDBConfigurationStore(
13 | this IIdentityServerBuilder builder,
14 | IDocumentStore documentStore, string databaseName,
15 | Action setup
16 | )
17 | {
18 | var session = documentStore.OpenAsyncSession(databaseName);
19 |
20 | builder.Services
21 | .AddTransient((sp) => new ClientStore(session))
22 | .AddTransient((sp) => new ResourceStore(session));
23 |
24 | setup(new ClientStore(session), new ResourceStore(session));
25 |
26 | return builder;
27 | }
28 |
29 | public static IIdentityServerBuilder AddRavenDBOperationalStore(
30 | this IIdentityServerBuilder builder,
31 | IDocumentStore documentStore, string databaseName
32 | )
33 | {
34 | var session = documentStore.OpenAsyncSession(databaseName);
35 |
36 | builder.Services
37 | .AddScoped(sp => new PersistedGrantStore(session));
38 |
39 | return builder;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mediatr/Exceptions/ThrowMediatrPipelineException.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using BuildingBlocks.Mediatr.Validators;
3 | using FluentValidation;
4 | using FluentValidation.Results;
5 |
6 | namespace BuildingBlocks.Mediatr.Exceptions
7 | {
8 | [DebuggerStepThrough]
9 | public static class ThrowMediatrPipelineException
10 | {
11 | public static void IdentifiedCommandWithoutId()
12 | {
13 | throw new MediatrPipelineException(
14 | "Invalid IdentifiedCommand",
15 | new ValidationException("Validation Exception",
16 | new[] { new ValidationFailure("Id", "You need to specify a valid id.") }
17 | )
18 | );
19 | }
20 |
21 | public static void IdentifiedCommandWithoutInnerCommand()
22 | {
23 | throw new MediatrPipelineException(
24 | "Invalid IdentifiedCommand",
25 | new ValidationException(
26 | "Validation Exception",
27 | new[] { new ValidationFailure("Command", "You need to specify a valid command to run.") }
28 | )
29 | );
30 | }
31 |
32 | public static void CommandWasAlreadyExecuted()
33 | {
34 | throw new MediatrPipelineException(
35 | "Invalid IdentifiedCommand",
36 | new ValidationException("Validation Exception",
37 | new[]
38 | {
39 | new ValidationFailure("Id", "This command was already executed.")
40 | }));
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/src/BuildingBlocks.IdentityServer4.RavenDB/Stores/ClientStore.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using IdentityServer4.Models;
3 | using IdentityServer4.Stores;
4 | using Raven.Client.Documents;
5 | using Raven.Client.Documents.Session;
6 |
7 | namespace BuildingBlocks.IdentityServer4.RavenDB.Stores
8 | {
9 | public interface IRavenDBClientStore : IClientStore
10 | {
11 | Task StoreAsync(Client client);
12 | Task HasStoredClients();
13 | }
14 | internal class ClientStore : IRavenDBClientStore
15 | {
16 | private readonly IAsyncDocumentSession _session;
17 |
18 | public ClientStore(IAsyncDocumentSession session)
19 | {
20 | _session = session;
21 | }
22 |
23 | public async Task FindClientByIdAsync(string clientId) =>
24 | (await _session.LoadAsync(RavenClient.DocId(clientId)));
25 |
26 | public async Task StoreAsync(Client client)
27 | {
28 | await _session.StoreAsync(client, RavenClient.DocId(client.ClientId));
29 | await _session.SaveChangesAsync();
30 | }
31 |
32 | public Task HasStoredClients()
33 | {
34 | return _session.Query()
35 | .Customize(o => o.WaitForNonStaleResults())
36 | .AnyAsync();
37 | }
38 |
39 | internal class RavenClient : Client
40 | {
41 | public string Id => DocId(ClientId);
42 |
43 | public static string DocId(string clientId)
44 | {
45 | return $"client/{clientId}";
46 | }
47 | }
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mediatr/Commands/IdentifiedCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using MediatR;
5 |
6 | using BuildingBlocks.Idempotency;
7 | using BuildingBlocks.Mediatr.Exceptions;
8 |
9 | namespace BuildingBlocks.Mediatr.Commands
10 | {
11 | public class IdentifiedCommandHandler
12 | : IRequestHandler, TResult>
13 | where TCommand : IRequest
14 | {
15 | private readonly IMediator _mediator;
16 | private readonly IRequestManager _requestManager;
17 |
18 | public IdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager)
19 | {
20 | _mediator = mediator;
21 | _requestManager = requestManager;
22 | }
23 |
24 | public async Task Handle(
25 | IdentifiedCommand message,
26 | CancellationToken cancellationToken = default(CancellationToken)
27 | )
28 | {
29 | if (message.Id == Guid.Empty)
30 | {
31 | ThrowMediatrPipelineException.IdentifiedCommandWithoutId();
32 | }
33 |
34 | if (message.Command == null)
35 | {
36 | ThrowMediatrPipelineException.IdentifiedCommandWithoutInnerCommand();
37 | }
38 |
39 | var alreadyRegistered = await _requestManager.IsRegistered(message.Id, cancellationToken);
40 | if (alreadyRegistered)
41 | {
42 | ThrowMediatrPipelineException.CommandWasAlreadyExecuted();
43 | }
44 |
45 | await _requestManager.Register(message.Id, cancellationToken);
46 | var result = await _mediator.Send(message.Command, cancellationToken);
47 | return result;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.AspnetCoreIdentity.RavenDB/User.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Security.Claims;
4 |
5 | namespace BuildingBlocks.AspnetCoreIdentity.RavenDB
6 | {
7 | public class User
8 | {
9 | private readonly List _claims = new List();
10 |
11 | public string Id { get; set; }
12 | public string Name { get; set; }
13 | public string NormalizedName { get; set; }
14 | public string PasswordHash { get; set; }
15 |
16 | public IEnumerable Claims
17 | {
18 | get => _claims;
19 | internal set
20 | {
21 | if (value != null) _claims.AddRange(value);
22 | }
23 | }
24 |
25 | internal void AddClaim(SimplifiedClaim claim)
26 | {
27 | if (claim == null)
28 | {
29 | throw new ArgumentNullException(nameof(claim));
30 | }
31 |
32 | _claims.Add(claim);
33 | }
34 |
35 | internal void RemoveClaim(SimplifiedClaim claim)
36 | {
37 | _claims.Remove(claim);
38 | }
39 | }
40 |
41 | public class SimplifiedClaim : IEquatable, IEquatable
42 | {
43 | public string Type { get; set; }
44 | public string Value { get; set; }
45 |
46 | public static implicit operator SimplifiedClaim(Claim original) =>
47 | new SimplifiedClaim { Type = original.Type, Value = original.Value };
48 |
49 | public static implicit operator Claim(SimplifiedClaim simplified) =>
50 | new Claim(simplified.Type, simplified.Value);
51 |
52 | public bool Equals(SimplifiedClaim other)
53 | => Type == other.Type && Value == other.Value;
54 |
55 | public bool Equals(Claim other)
56 | => Type == other.Type && Value == other.Value;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Swagger/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.Extensions.DependencyInjection;
3 |
4 | using Swashbuckle.AspNetCore.Examples;
5 | using Swashbuckle.AspNetCore.Swagger;
6 |
7 | using BuildingBlocks.Core;
8 |
9 | namespace BuildingBlocks.Swagger
10 | {
11 | public static class Startup
12 | {
13 | public static IServiceCollection AddCustomSwagger(
14 | this IServiceCollection services,
15 | IApiInfo apiInfo
16 |
17 | ) => services
18 | .AddSwaggerGen(options =>
19 | {
20 | options.DescribeAllEnumsAsStrings();
21 |
22 | options.SwaggerDoc(apiInfo.Version, new Info
23 | {
24 | Title = apiInfo.Title,
25 | Version = apiInfo.Version,
26 | Description = apiInfo.Version
27 | });
28 |
29 | if (apiInfo.AuthenticationAuthority != null)
30 | {
31 | options.AddSecurityDefinition("oauth2", new OAuth2Scheme
32 | {
33 | Type = "oauth2",
34 | Flow = "implicit",
35 | AuthorizationUrl = $"{apiInfo.AuthenticationAuthority}/connect/authorize",
36 | TokenUrl = $"{apiInfo.AuthenticationAuthority}/connect/token",
37 | Scopes = apiInfo.Scopes
38 | });
39 | }
40 |
41 | options.OperationFilter(apiInfo);
42 | options.OperationFilter();
43 | options.OperationFilter();
44 | });
45 |
46 | public static IApplicationBuilder UseCustomSwagger(
47 | this IApplicationBuilder app,
48 | IApiInfo apiInfo
49 | ) => app
50 | .UseSwagger()
51 | .UseSwaggerUI(c =>
52 | {
53 | c.SwaggerEndpoint($"/swagger/{apiInfo.Version}/swagger.json", $"{apiInfo.Title} {apiInfo.Version}");
54 | if (apiInfo.AuthenticationAuthority != null)
55 | {
56 | c.ConfigureOAuth2(
57 | apiInfo.SwaggerAuthInfo.ClientId,
58 | apiInfo.SwaggerAuthInfo.Secret,
59 | apiInfo.SwaggerAuthInfo.Realm,
60 | $"{apiInfo.Title} - ${apiInfo.Version} - Swagger UI"
61 | );
62 | }
63 | });
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mediatr/Exceptions/HttpGlobalExceptionFilter.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Net;
3 | using BuildingBlocks.Mediatr.Validators;
4 | using FluentValidation;
5 | using Microsoft.AspNetCore.Hosting;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.AspNetCore.Mvc.Filters;
8 | using Microsoft.Extensions.Logging;
9 |
10 | namespace BuildingBlocks.Mediatr.Exceptions
11 | {
12 | public class HttpGlobalExceptionFilter : IExceptionFilter
13 | {
14 | private readonly IHostingEnvironment _env;
15 | private readonly ILogger _logger;
16 |
17 | public HttpGlobalExceptionFilter(
18 | IHostingEnvironment env,
19 | ILogger logger
20 | )
21 | {
22 | _env = env;
23 | _logger = logger;
24 | }
25 |
26 | public void OnException(ExceptionContext context)
27 | {
28 | _logger.LogError(new EventId(context.Exception.HResult),
29 | context.Exception,
30 | context.Exception.Message);
31 |
32 | if (context.Exception.GetType() == typeof(MediatrPipelineException))
33 | {
34 | var validationException = context.Exception.InnerException as ValidationException;
35 | if (validationException != null)
36 | {
37 | var json = new JsonErrorResponse
38 | {
39 | Messages = validationException.Errors
40 | .Select(e => e.ErrorMessage)
41 | .ToArray()
42 | };
43 |
44 | context.Result = new BadRequestObjectResult(json);
45 | }
46 | context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
47 | }
48 | else
49 | {
50 | var json = new JsonErrorResponse
51 | {
52 | Messages = new[]
53 | {
54 | "Internal Error. Try again later.",
55 | context.Exception.GetType().ToString(),
56 | context.Exception.Message
57 | }
58 | };
59 |
60 | context.Result = new ObjectResult(json) { StatusCode = 500 };
61 | context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
62 | }
63 | context.ExceptionHandled = true;
64 | }
65 |
66 | public class JsonErrorResponse
67 | {
68 | public string[] Messages { get; set; }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/BuindingBlocks.Resilience/Http/ResilientHttpClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Net.Http;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using Microsoft.AspNetCore.Http;
9 | using Polly;
10 | using Polly.Wrap;
11 |
12 | namespace BuindingBlocks.Resilience.Http
13 | {
14 | public delegate IEnumerable PolicyFactory(string origin);
15 |
16 | public class ResilientHttpClient : IHttpClient
17 | {
18 | private readonly PolicyFactory _policyFactory;
19 | private readonly StandardHttpClient _standardHttpClient;
20 |
21 | private readonly ConcurrentDictionary _policyWrappers =
22 | new ConcurrentDictionary();
23 |
24 | public ResilientHttpClient(
25 | PolicyFactory policyFactory,
26 | IHttpContextAccessor accessor
27 | )
28 | {
29 | _policyFactory = policyFactory;
30 | _standardHttpClient = new StandardHttpClient(accessor);
31 |
32 | }
33 |
34 | public Task GetStringAsync(string uri, Authorization authorization = default(Authorization),
35 | CancellationToken cancellationToken = default(CancellationToken)) => HttpInvoker(
36 | new Uri(uri).GetOriginFromUri(),
37 | () => _standardHttpClient.GetStringAsync(
38 | uri, authorization, cancellationToken
39 | )
40 | );
41 |
42 | public Task PutAsync(string uri, T item, string requestId = null, Authorization authorization = default(Authorization),
43 | CancellationToken cancellationToken = default(CancellationToken)) => HttpInvoker(
44 | new Uri(uri).GetOriginFromUri(),
45 | () => _standardHttpClient.PutAsync(
46 | uri, item, requestId, authorization, cancellationToken
47 | )
48 | );
49 |
50 | private async Task HttpInvoker(string origin, Func> action)
51 | {
52 | var normalizedOrigin = origin?.Trim()?.ToLower();
53 |
54 | if (!_policyWrappers.TryGetValue(normalizedOrigin, out var policyWrap))
55 | {
56 | var policies = _policyFactory(normalizedOrigin).ToArray();
57 | policyWrap = Policy.WrapAsync(policies);
58 | _policyWrappers.TryAdd(normalizedOrigin, policyWrap);
59 | }
60 |
61 | return await policyWrap.ExecuteAsync(action, new Context(normalizedOrigin));
62 | }
63 | }
64 |
65 | public static class UriExtensions
66 | {
67 | public static string GetOriginFromUri(this Uri uri) =>
68 | $"{uri.Scheme}://{uri.DnsSafeHost}:{uri.Port}";
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/BuindingBlocks.Resilience/Http/StandardHttpClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using Microsoft.AspNetCore.Http;
4 | using System.Net.Http;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using Newtonsoft.Json;
8 |
9 | namespace BuindingBlocks.Resilience.Http
10 | {
11 | public class StandardHttpClient : IHttpClient, IDisposable
12 | {
13 | private readonly IHttpContextAccessor _httpContextAccessor;
14 | private readonly HttpClient _httpClient;
15 |
16 | public StandardHttpClient(IHttpContextAccessor httpContextAccessor)
17 | {
18 | _httpContextAccessor = httpContextAccessor;
19 | _httpClient = new HttpClient();
20 | }
21 |
22 | public async Task GetStringAsync(
23 | string uri,
24 | Authorization authorization = default(Authorization),
25 | CancellationToken cancellationToken = default(CancellationToken)
26 | )
27 | {
28 | var message = new HttpRequestMessage(HttpMethod.Get, uri)
29 | .CopyAuthorizationHeaderFrom(_httpContextAccessor.HttpContext)
30 | .Apply(authorization);
31 |
32 | var response = await _httpClient.SendAsync(message, cancellationToken);
33 |
34 | if (response.StatusCode == HttpStatusCode.InternalServerError)
35 | {
36 | throw new HttpRequestException();
37 | }
38 |
39 | return await response.Content.ReadAsStringAsync();
40 | }
41 |
42 | public async Task PutAsync(
43 | string uri,
44 | T item, string requestId = null,
45 | Authorization authorization = default(Authorization),
46 | CancellationToken cancellationToken = default(CancellationToken))
47 | {
48 | var requestMessage = new HttpRequestMessage(HttpMethod.Put, uri)
49 | .CopyAuthorizationHeaderFrom(_httpContextAccessor.HttpContext)
50 | .Apply(authorization);
51 |
52 | requestMessage.Content = new StringContent(
53 | JsonConvert.SerializeObject(item),
54 | System.Text.Encoding.UTF8, "application/json"
55 | );
56 |
57 | if (requestId != null)
58 | {
59 | requestMessage.Headers.Add("x-requestid", requestId);
60 | }
61 |
62 | var response = await _httpClient.SendAsync(requestMessage, cancellationToken);
63 |
64 | if (response.StatusCode == HttpStatusCode.InternalServerError)
65 | {
66 | throw new HttpRequestException();
67 | }
68 |
69 | return response;
70 | }
71 |
72 | public void Dispose()
73 | {
74 | _httpClient?.Dispose();
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.IdentityServer4.RavenDB/Stores/PersistedGrantStore.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using IdentityServer4.Models;
5 | using IdentityServer4.Stores;
6 |
7 | using Raven.Client.Documents;
8 | using Raven.Client.Documents.Linq;
9 | using Raven.Client.Documents.Session;
10 |
11 | namespace BuildingBlocks.IdentityServer4.RavenDB.Stores
12 | {
13 | internal class PersistedGrantStore : IPersistedGrantStore
14 | {
15 | private readonly IAsyncDocumentSession _session;
16 |
17 | public PersistedGrantStore(IAsyncDocumentSession session)
18 | {
19 | _session = session;
20 | }
21 |
22 | public async Task StoreAsync(PersistedGrant grant)
23 | {
24 | await _session.StoreAsync(grant, RavenPersistedGrant.DocId(grant.Key));
25 | await _session.SaveChangesAsync();
26 | }
27 |
28 | public async Task GetAsync(string key) =>
29 | (await _session.LoadAsync(RavenPersistedGrant.DocId(key)));
30 |
31 | public async Task> GetAllAsync(string subjectId)
32 | {
33 | var grants = await _session
34 | .Query()
35 | .Customize(opt => opt.WaitForNonStaleResults())
36 | .Where(g => g.SubjectId == subjectId)
37 | .ToListAsync();
38 |
39 | return grants;
40 | }
41 |
42 |
43 | public async Task RemoveAsync(string key)
44 | {
45 | _session.Delete(RavenPersistedGrant.DocId(key));
46 | await _session.SaveChangesAsync();
47 | }
48 |
49 | public async Task RemoveAllAsync(string subjectId, string clientId)
50 | {
51 | await RemoveWhereAsync(g => (g.SubjectId == subjectId) && (g.ClientId == clientId));
52 | }
53 |
54 | public async Task RemoveAllAsync(string subjectId, string clientId, string type)
55 | {
56 | await RemoveWhereAsync(g => (g.SubjectId == subjectId) && (g.ClientId == clientId) && (g.Type == type));
57 | }
58 |
59 | private async Task RemoveWhereAsync(Func @where)
60 | {
61 | var itemsToDelete =
62 | await _session
63 | .Query()
64 | .Customize(opt => opt.WaitForNonStaleResults())
65 | .Where(g => @where.Invoke(g))
66 | .ToListAsync();
67 |
68 | foreach (var grant in itemsToDelete)
69 | _session.Delete(grant);
70 |
71 | await _session.SaveChangesAsync();
72 | }
73 |
74 | internal class RavenPersistedGrant : PersistedGrant
75 | {
76 | public string Id => DocId(Key);
77 |
78 | public static string DocId(string key)
79 | {
80 | return $"scope/{key}";
81 | }
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.IdentityServer4.RavenDB/Stores/ResourceStore.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using IdentityServer4.Models;
5 | using IdentityServer4.Stores;
6 | using Raven.Client.Documents;
7 | using Raven.Client.Documents.Linq;
8 | using Raven.Client.Documents.Session;
9 |
10 | namespace BuildingBlocks.IdentityServer4.RavenDB.Stores
11 | {
12 | public interface IRavenDBResourceStore : IResourceStore
13 | {
14 | Task StoreAsync(IdentityResource identity);
15 | Task StoreAsync(ApiResource api);
16 | Task HasStoredIdentities();
17 | Task HasStoredApis();
18 | }
19 |
20 | internal class ResourceStore : IRavenDBResourceStore
21 | {
22 | private readonly IAsyncDocumentSession _session;
23 |
24 | public ResourceStore(IAsyncDocumentSession session)
25 | {
26 | _session = session;
27 | }
28 |
29 | public async Task> FindIdentityResourcesByScopeAsync(IEnumerable scopeNames)
30 | {
31 | var scopes = scopeNames.ToArray();
32 |
33 | var identities = await _session.Query()
34 | .Where(r => r.Name.In(scopes))
35 | .ToListAsync();
36 |
37 | return identities; //.Select(identity => identity.ToModel());
38 | }
39 |
40 | public async Task> FindApiResourcesByScopeAsync(IEnumerable scopeNames)
41 | {
42 | var scopes = scopeNames.ToArray();
43 |
44 | var apis = await _session.Query()
45 | .Where(r => r.Name.In(scopes))
46 | .ToListAsync();
47 |
48 | return apis;
49 | }
50 |
51 | public async Task FindApiResourceAsync(string name)
52 | {
53 | var api = await _session.Query()
54 | .Where(r => r.Name == name)
55 | .FirstOrDefaultAsync();
56 |
57 | return api;
58 | }
59 |
60 | public async Task GetAllResourcesAsync()
61 | {
62 | var identities = await _session.Query()
63 | .ToListAsync();
64 |
65 | var apis = await _session.Query()
66 | .ToListAsync();
67 |
68 | var result = new Resources(
69 | identities, //.Select(identity => identity.ToModel()),
70 | apis //.Select(api => api.ToModel())
71 | );
72 |
73 | return result;
74 | }
75 |
76 | public async Task StoreAsync(IdentityResource identity)
77 | {
78 | await _session.StoreAsync(identity);
79 | await _session.SaveChangesAsync();
80 | }
81 |
82 | public async Task StoreAsync(ApiResource api)
83 | {
84 | await _session.StoreAsync(api);
85 | await _session.SaveChangesAsync();
86 | }
87 |
88 | public Task HasStoredIdentities()
89 | {
90 | return _session.Query()
91 | .Customize(o => o.WaitForNonStaleResults())
92 | .AnyAsync();
93 | }
94 |
95 | public Task HasStoredApis()
96 | {
97 | return _session.Query()
98 | .Customize(o => o.WaitForNonStaleResults())
99 | .AnyAsync();
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/BuildingBlocks.Mediatr/Autofac/MediatrModule.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Reflection;
4 |
5 | using FluentValidation;
6 | using MediatR;
7 | using Autofac;
8 | using Module = Autofac.Module;
9 |
10 | using BuildingBlocks.Mediatr.Commands;
11 | using BuildingBlocks.Mediatr.Validators;
12 |
13 |
14 | namespace BuildingBlocks.Mediatr.Autofac
15 | {
16 | public class MediatrModule : Module
17 | {
18 | private readonly Assembly _applicationAssembly;
19 |
20 | private MediatrModule(Assembly applicationAssembly)
21 | {
22 | _applicationAssembly = applicationAssembly;
23 | }
24 |
25 | public static MediatrModule Create(Assembly applicationAssembly)
26 | => new MediatrModule(applicationAssembly);
27 |
28 | protected override void Load(ContainerBuilder builder)
29 | {
30 | builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly)
31 | .AsImplementedInterfaces();
32 |
33 | builder.RegisterAssemblyTypes(_applicationAssembly)
34 | .AsClosedTypesOf(typeof(IRequestHandler<,>));
35 |
36 | var handlers = _applicationAssembly.GetTypes()
37 | .Where(t => t.IsClosedTypeOf(typeof(IRequestHandler<,>)))
38 | .ToList();
39 |
40 | handlers.ForEach(t =>
41 | {
42 | var localHandlers = t.GetInterfaces()
43 | .Where(@interface => @interface.IsClosedTypeOf(typeof(IRequestHandler<,>)));
44 |
45 | foreach (var localHandler in localHandlers)
46 | {
47 | var implementation = typeof(IdentifiedCommandHandler<,>)
48 | .MakeGenericType(localHandler.GenericTypeArguments);
49 |
50 | var arg0 = typeof(IdentifiedCommand<,>)
51 | .MakeGenericType(localHandler.GenericTypeArguments);
52 | var arg1 = localHandler.GenericTypeArguments[1];
53 |
54 | var service = typeof(IRequestHandler<,>)
55 | .MakeGenericType(arg0, arg1);
56 |
57 | builder.RegisterType(implementation).As(service);
58 | }
59 | });
60 |
61 | var sharedLogicMediatr = typeof(IdentifiedCommand<,>).GetTypeInfo().Assembly;
62 | builder.RegisterAssemblyTypes(sharedLogicMediatr)
63 | .AsClosedTypesOf(typeof(IRequestHandler<,>));
64 |
65 | builder
66 | .RegisterAssemblyTypes(sharedLogicMediatr)
67 | .Where(t => t.IsClosedTypeOf(typeof(IValidator<>)))
68 | .AsImplementedInterfaces();
69 |
70 | builder
71 | .RegisterAssemblyTypes(_applicationAssembly)
72 | .AsClosedTypesOf(typeof(IValidator<>));
73 |
74 | builder.Register(context =>
75 | {
76 | var componentContext = context.Resolve();
77 | return t =>
78 | {
79 | return componentContext.TryResolve(t, out var o) ? o : null;
80 | };
81 | });
82 |
83 | builder.Register(context =>
84 | {
85 | var componentContext = context.Resolve();
86 |
87 | return t =>
88 | {
89 | var resolved = (IEnumerable