Skip to content

Commit

Permalink
Merge pull request abpframework#12896 from abpframework/integration-s…
Browse files Browse the repository at this point in the history
…ervices

Introduce Integration Service Infrastructure
  • Loading branch information
maliming authored Sep 9, 2022
2 parents 745d374 + 17a260f commit c78497a
Show file tree
Hide file tree
Showing 24 changed files with 250 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,14 @@ public static bool IsPageAction(this ActionDescriptor actionDescriptor)
{
return actionDescriptor is PageActionDescriptor;
}

public static PageActionDescriptor AsPageAction(this ActionDescriptor actionDescriptor)
{
if (!actionDescriptor.IsPageAction())
{
throw new AbpException($"{nameof(actionDescriptor)} should be type of {typeof(PageActionDescriptor).AssemblyQualifiedName}");
}

return actionDescriptor as PageActionDescriptor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ private bool ShouldSaveAudit(ActionExecutingContext context, out AuditLogInfo au
}

var auditingHelper = context.GetRequiredService<IAuditingHelper>();
if (!auditingHelper.ShouldSaveAudit(context.ActionDescriptor.GetMethodInfo(), true))
if (!auditingHelper.ShouldSaveAudit(
context.ActionDescriptor.GetMethodInfo(),
defaultValue: GetDefaultAuditBehavior(options, context.ActionDescriptor)))
{
return false;
}
Expand All @@ -85,4 +87,20 @@ private bool ShouldSaveAudit(ActionExecutingContext context, out AuditLogInfo au

return true;
}

private static bool GetDefaultAuditBehavior(
AbpAuditingOptions abpAuditingOptions,
ActionDescriptor actionDescriptor)
{
if (!abpAuditingOptions.IsEnabledForIntegrationServices &&
actionDescriptor
.AsControllerActionDescriptor()
.ControllerTypeInfo
.IsDefined(typeof(IntegrationServiceAttribute), true))
{
return false;
}

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ private bool ShouldSaveAudit(PageHandlerExecutingContext context, out AuditLogIn
}

var auditingHelper = context.GetRequiredService<IAuditingHelper>();
if (!auditingHelper.ShouldSaveAudit(context.HandlerMethod.MethodInfo, true))
if (!auditingHelper.ShouldSaveAudit(context.HandlerMethod.MethodInfo, defaultValue: true))
{
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ public virtual string Build(
string httpMethod,
[CanBeNull] ConventionalControllerSetting configuration)
{
var controllerNameInUrl = NormalizeUrlControllerName(rootPath, controllerName, action, httpMethod, configuration);
var apiRoutePrefix = GetApiRoutePrefix(action, configuration);
var controllerNameInUrl =
NormalizeUrlControllerName(rootPath, controllerName, action, httpMethod, configuration);

var url = $"api/{rootPath}/{NormalizeControllerNameCase(controllerNameInUrl, configuration)}";
var url = $"{apiRoutePrefix}/{rootPath}/{NormalizeControllerNameCase(controllerNameInUrl, configuration)}";

//Add {id} path if needed
var idParameterModel = action.Parameters.FirstOrDefault(p => p.ParameterName == "id");
Expand Down Expand Up @@ -69,6 +71,16 @@ public virtual string Build(
return url;
}

protected virtual string GetApiRoutePrefix(ActionModel actionModel, ConventionalControllerSetting configuration)
{
if (actionModel.Controller.ControllerType.IsDefined(typeof(IntegrationServiceAttribute), true))
{
return AbpAspNetCoreConsts.DefaultIntegrationServiceApiPrefix;
}

return AbpAspNetCoreConsts.DefaultApiPrefix;
}

protected virtual string NormalizeUrlActionName(string rootPath, string controllerName, ActionModel action,
string httpMethod, [CanBeNull] ConventionalControllerSetting configuration)
{
Expand Down Expand Up @@ -108,7 +120,8 @@ protected virtual string NormalizeUrlControllerName(string rootPath, string cont
);
}

protected virtual string NormalizeControllerNameCase(string controllerName, [CanBeNull] ConventionalControllerSetting configuration)
protected virtual string NormalizeControllerNameCase(string controllerName,
[CanBeNull] ConventionalControllerSetting configuration)
{
if (configuration?.UseV3UrlStyle ?? Options.UseV3UrlStyle)
{
Expand All @@ -120,7 +133,8 @@ protected virtual string NormalizeControllerNameCase(string controllerName, [Can
}
}

protected virtual string NormalizeActionNameCase(string actionName, [CanBeNull] ConventionalControllerSetting configuration)
protected virtual string NormalizeActionNameCase(string actionName,
[CanBeNull] ConventionalControllerSetting configuration)
{
if (configuration?.UseV3UrlStyle ?? Options.UseV3UrlStyle)
{
Expand All @@ -132,13 +146,15 @@ protected virtual string NormalizeActionNameCase(string actionName, [CanBeNull]
}
}

protected virtual string NormalizeIdPropertyNameCase(PropertyInfo property, [CanBeNull] ConventionalControllerSetting configuration)
protected virtual string NormalizeIdPropertyNameCase(PropertyInfo property,
[CanBeNull] ConventionalControllerSetting configuration)
{
return property.Name;
}

protected virtual string NormalizeSecondaryIdNameCase(ParameterModel secondaryId, [CanBeNull] ConventionalControllerSetting configuration)
protected virtual string NormalizeSecondaryIdNameCase(ParameterModel secondaryId,
[CanBeNull] ConventionalControllerSetting configuration)
{
return secondaryId.ParameterName;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Volo.Abp.AspNetCore;

public static class AbpAspNetCoreConsts
{
public const string DefaultApiPrefix = "api";
public const string DefaultIntegrationServiceApiPrefix = "integration-api";
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ public class AbpAspNetCoreAuditingOptions
/// <see cref="AbpAuditingMiddleware"/> will be disabled for URLs
/// starting with an ignored URL.
/// </summary>
public List<string> IgnoredUrls { get; } = new List<string>();
public List<string> IgnoredUrls { get; } = new();
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,23 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next)

private bool IsIgnoredUrl(HttpContext context)
{
return context.Request.Path.Value != null &&
AspNetCoreAuditingOptions.IgnoredUrls.Any(x => context.Request.Path.Value.StartsWith(x));
if (context.Request.Path.Value == null)
{
return false;
}

if (!AuditingOptions.IsEnabledForIntegrationServices &&
context.Request.Path.Value.StartsWith($"/{AbpAspNetCoreConsts.DefaultIntegrationServiceApiPrefix}/"))
{
return true;
}

if (AspNetCoreAuditingOptions.IgnoredUrls.Any(x => context.Request.Path.Value.StartsWith(x)))
{
return true;
}

return false;
}

private async Task<bool> ShouldWriteAuditLogAsync(AuditLogInfo auditLogInfo, HttpContext httpContext, bool hasError)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ public class AbpAuditingOptions
/// Default: true.
/// </summary>
public bool AlwaysLogOnException { get; set; }

/// <summary>
/// Disables/enables audit logging for integration services.
/// Default: false.
/// </summary>
public bool IsEnabledForIntegrationServices { get; set; }

public List<Func<AuditLogInfo, Task<bool>>> AlwaysLogSelectors { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public AuditingHelper(
CorrelationIdProvider = correlationIdProvider;
}

public virtual bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = false)
public virtual bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = false, bool ignoreIntegrationServiceAttribute = false)
{
if (methodInfo == null)
{
Expand All @@ -77,7 +77,7 @@ public virtual bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = f
var classType = methodInfo.DeclaringType;
if (classType != null)
{
var shouldAudit = AuditingInterceptorRegistrar.ShouldAuditTypeByDefaultOrNull(classType);
var shouldAudit = AuditingInterceptorRegistrar.ShouldAuditTypeByDefaultOrNull(classType, ignoreIntegrationServiceAttribute);
if (shouldAudit != null)
{
return shouldAudit.Value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ protected virtual bool ShouldIntercept(IAbpMethodInvocation invocation,
return false;
}

if (!auditingHelper.ShouldSaveAudit(invocation.Method))
if (!auditingHelper.ShouldSaveAudit(
invocation.Method,
ignoreIntegrationServiceAttribute: options.IsEnabledForIntegrationServices))
{
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ private static bool ShouldIntercept(Type type)
return false;
}

if (ShouldAuditTypeByDefaultOrNull(type) == true)
if (ShouldAuditTypeByDefaultOrNull(type, ignoreIntegrationServiceAttribute: true) == true)
{
return true;
}
Expand All @@ -36,7 +36,7 @@ private static bool ShouldIntercept(Type type)
}

//TODO: Move to a better place
public static bool? ShouldAuditTypeByDefaultOrNull(Type type)
public static bool? ShouldAuditTypeByDefaultOrNull(Type type, bool ignoreIntegrationServiceAttribute)
{
//TODO: In an inheritance chain, it would be better to check the attributes on the top class first.

Expand All @@ -52,7 +52,10 @@ private static bool ShouldIntercept(Type type)

if (typeof(IAuditingEnabled).IsAssignableFrom(type))
{
return true;
if (ignoreIntegrationServiceAttribute || !type.IsDefined(typeof(IntegrationServiceAttribute), true))
{
return true;
}
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Volo.Abp.Auditing;
//TODO: Move ShouldSaveAudit & IsEntityHistoryEnabled and rename to IAuditingFactory
public interface IAuditingHelper
{
bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = false);
bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = false, bool ignoreIntegrationServiceAttribute = false);

bool IsEntityHistoryEnabled(Type entityType, bool defaultValue = false);

Expand Down
2 changes: 1 addition & 1 deletion framework/src/Volo.Abp.Core/Volo/Abp/IRemoteService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
namespace Volo.Abp;

public interface IRemoteService //TODO: Can we move this to another package?
public interface IRemoteService
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;

namespace Volo.Abp;

[AttributeUsage(AttributeTargets.Class)]
public class IntegrationServiceAttribute : Attribute
{

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
/// <summary>
/// This interface must be implemented by all application services to register and identify them by convention.
/// </summary>
public interface IApplicationService :
IRemoteService
public interface IApplicationService : IRemoteService
{

}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using Volo.Abp.Application.Services;
using Volo.Abp.Aspects;
using Volo.Abp.Auditing;
using Volo.Abp.Authorization;
using Volo.Abp.Domain;
using Volo.Abp.Features;
Expand Down Expand Up @@ -33,10 +35,12 @@ public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpApiDescriptionModelOptions>(options =>
{
//TODO: Should we move related items to their own projects?
options.IgnoredInterfaces.AddIfNotContains(typeof(IRemoteService));
options.IgnoredInterfaces.AddIfNotContains(typeof(IRemoteService));
options.IgnoredInterfaces.AddIfNotContains(typeof(IApplicationService));
options.IgnoredInterfaces.AddIfNotContains(typeof(IUnitOfWorkEnabled));
options.IgnoredInterfaces.AddIfNotContains(typeof(IAuditingEnabled));
options.IgnoredInterfaces.AddIfNotContains(typeof(IValidationEnabled));
options.IgnoredInterfaces.AddIfNotContains(typeof(IGlobalFeatureCheckingEnabled));
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ public abstract class ApplicationService :
[Obsolete("Use LazyServiceProvider instead.")]
public IServiceProvider ServiceProvider { get; set; }

public static string[] CommonPostfixes { get; set; } = { "AppService", "ApplicationService", "Service" };
public static string[] CommonPostfixes { get; set; } = { "AppService", "ApplicationService", "IntService", "IntegrationService", "Service" };

public List<string> AppliedCrossCuttingConcerns { get; } = new List<string>();
public List<string> AppliedCrossCuttingConcerns { get; } = new();

protected IUnitOfWorkManager UnitOfWorkManager => LazyServiceProvider.LazyGetRequiredService<IUnitOfWorkManager>();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Mvc;

namespace Volo.Abp.AspNetCore.Mvc.Auditing;

[Route("integration-api/audit-test")]
[IntegrationService]
public class AuditIntegrationServiceTestController : AbpController
{
[HttpGet]
public IActionResult Get()
{
return Ok();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using NSubstitute;
using Shouldly;
using Volo.Abp.Auditing;
using Xunit;

namespace Volo.Abp.AspNetCore.Mvc.Auditing;

public class AuditIntegrationServiceTestController_Tests : AspNetCoreMvcTestBase
{
private readonly AbpAuditingOptions _options;
private IAuditingStore _auditingStore;

public AuditIntegrationServiceTestController_Tests()
{
_options = ServiceProvider.GetRequiredService<IOptions<AbpAuditingOptions>>().Value;
_auditingStore = ServiceProvider.GetRequiredService<IAuditingStore>();
}

protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
_auditingStore = Substitute.For<IAuditingStore>();
services.Replace(ServiceDescriptor.Singleton(_auditingStore));
base.ConfigureServices(context, services);
}

[Fact]
public async Task Should_Write_Audit_Log_For_Controllers_With_IntegrationService_Attribute_If_IsEnabledForIntegrationServices()
{
_options.IsEnabledForGetRequests = true;
_options.IsEnabledForIntegrationServices = true;
await GetResponseAsync("/integration-api/audit-test/");
await _auditingStore
.Received()
.SaveAsync(
Arg.Is<AuditLogInfo>(
x => x.Actions.Any(
a =>
a.MethodName == nameof(AuditIntegrationServiceTestController.Get) &&
a.ServiceName == typeof(AuditIntegrationServiceTestController).FullName
)
)
);
}

[Fact]
public async Task Should_Not_Write_Audit_Log_For_Controllers_With_IntegrationService_Attribute()
{
_options.IsEnabledForGetRequests = true;
await GetResponseAsync("/integration-api/audit-test/");
await _auditingStore.DidNotReceive().SaveAsync(Arg.Any<AuditLogInfo>());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Linq;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
Expand Down
Loading

0 comments on commit c78497a

Please sign in to comment.