From d2e52f64f284eaca0191f8430c11c10c1e890543 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 9 Jul 2021 15:18:10 +0800 Subject: [PATCH 01/14] Make IObjectValidator async. Resolve #9544 --- .../FluentObjectValidationContributor.cs | 5 ++-- ...taAnnotationObjectValidationContributor.cs | 4 ++- .../Validation/IMethodInvocationValidator.cs | 6 +++-- .../IObjectValidationContributor.cs | 8 +++--- .../Volo/Abp/Validation/IObjectValidator.cs | 7 ++--- .../Validation/MethodInvocationValidator.cs | 17 ++++++------ .../Volo/Abp/Validation/ObjectValidator.cs | 9 ++++--- .../Abp/Validation/ValidationInterceptor.cs | 6 ++--- ...plicationService_FluentValidation_Tests.cs | 27 ++++++++++++++----- 9 files changed, 57 insertions(+), 32 deletions(-) diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentObjectValidationContributor.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentObjectValidationContributor.cs index ccc290f354..e2d0f39372 100644 --- a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentObjectValidationContributor.cs +++ b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentObjectValidationContributor.cs @@ -2,6 +2,7 @@ using FluentValidation; using System; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Validation; @@ -17,7 +18,7 @@ namespace Volo.Abp.FluentValidation _serviceProvider = serviceProvider; } - public void AddErrors(ObjectValidationContext context) + public virtual async Task AddErrorsAsync(ObjectValidationContext context) { var serviceType = typeof(IValidator<>).MakeGenericType(context.ValidatingObject.GetType()); var validator = _serviceProvider.GetService(serviceType) as IValidator; @@ -26,7 +27,7 @@ namespace Volo.Abp.FluentValidation return; } - var result = validator.Validate((IValidationContext) Activator.CreateInstance( + var result = await validator.ValidateAsync((IValidationContext) Activator.CreateInstance( typeof(ValidationContext<>).MakeGenericType(context.ValidatingObject.GetType()), context.ValidatingObject)); diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationObjectValidationContributor.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationObjectValidationContributor.cs index 912f68e4af..15e367cefc 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationObjectValidationContributor.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationObjectValidationContributor.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; @@ -26,9 +27,10 @@ namespace Volo.Abp.Validation Options = options.Value; } - public void AddErrors(ObjectValidationContext context) + public Task AddErrorsAsync(ObjectValidationContext context) { ValidateObjectRecursively(context.Errors, context.ValidatingObject, currentDepth: 1); + return Task.CompletedTask; } protected virtual void ValidateObjectRecursively(List errors, object validatingObject, int currentDepth) diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IMethodInvocationValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IMethodInvocationValidator.cs index fd413bc498..89eec62116 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IMethodInvocationValidator.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IMethodInvocationValidator.cs @@ -1,7 +1,9 @@ -namespace Volo.Abp.Validation +using System.Threading.Tasks; + +namespace Volo.Abp.Validation { public interface IMethodInvocationValidator { - void Validate(MethodInvocationValidationContext context); + Task ValidateAsync(MethodInvocationValidationContext context); } } diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidationContributor.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidationContributor.cs index ca50901bcd..45d0ddcd9b 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidationContributor.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidationContributor.cs @@ -1,7 +1,9 @@ -namespace Volo.Abp.Validation +using System.Threading.Tasks; + +namespace Volo.Abp.Validation { public interface IObjectValidationContributor { - void AddErrors(ObjectValidationContext context); + Task AddErrorsAsync(ObjectValidationContext context); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs index 0ce723bab8..1b7c5881ee 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs @@ -1,20 +1,21 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; namespace Volo.Abp.Validation { public interface IObjectValidator { - void Validate( + Task ValidateAsync( object validatingObject, string name = null, bool allowNull = false ); - List GetErrors( + Task> GetErrorsAsync( object validatingObject, string name = null, bool allowNull = false ); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/MethodInvocationValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/MethodInvocationValidator.cs index 584f787a6e..3aeaf57fc7 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/MethodInvocationValidator.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/MethodInvocationValidator.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Reflection; @@ -16,7 +17,7 @@ namespace Volo.Abp.Validation _objectValidator = objectValidator; } - public virtual void Validate(MethodInvocationValidationContext context) + public virtual async Task ValidateAsync(MethodInvocationValidationContext context) { Check.NotNull(context, nameof(context)); @@ -46,7 +47,7 @@ namespace Volo.Abp.Validation ThrowValidationError(context); } - AddMethodParameterValidationErrors(context); + await AddMethodParameterValidationErrorsAsync(context); if (context.Errors.Any()) { @@ -60,7 +61,7 @@ namespace Volo.Abp.Validation { return false; } - + if (ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(context.Method) != null) { return true; @@ -82,22 +83,22 @@ namespace Volo.Abp.Validation ); } - protected virtual void AddMethodParameterValidationErrors(MethodInvocationValidationContext context) + protected virtual async Task AddMethodParameterValidationErrorsAsync(MethodInvocationValidationContext context) { for (var i = 0; i < context.Parameters.Length; i++) { - AddMethodParameterValidationErrors(context, context.Parameters[i], context.ParameterValues[i]); + await AddMethodParameterValidationErrorsAsync(context, context.Parameters[i], context.ParameterValues[i]); } } - protected virtual void AddMethodParameterValidationErrors(IAbpValidationResult context, ParameterInfo parameterInfo, object parameterValue) + protected virtual async Task AddMethodParameterValidationErrorsAsync(IAbpValidationResult context, ParameterInfo parameterInfo, object parameterValue) { var allowNulls = parameterInfo.IsOptional || parameterInfo.IsOut || TypeHelper.IsPrimitiveExtended(parameterInfo.ParameterType, includeEnums: true); context.Errors.AddRange( - _objectValidator.GetErrors( + await _objectValidator.GetErrorsAsync( parameterValue, parameterInfo.Name, allowNulls @@ -105,4 +106,4 @@ namespace Volo.Abp.Validation ); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs index d8e0c60b65..8e13d72938 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Options; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.DependencyInjection; @@ -18,9 +19,9 @@ namespace Volo.Abp.Validation Options = options.Value; } - public virtual void Validate(object validatingObject, string name = null, bool allowNull = false) + public virtual async Task ValidateAsync(object validatingObject, string name = null, bool allowNull = false) { - var errors = GetErrors(validatingObject, name, allowNull); + var errors = await GetErrorsAsync(validatingObject, name, allowNull); if (errors.Any()) { @@ -31,7 +32,7 @@ namespace Volo.Abp.Validation } } - public virtual List GetErrors(object validatingObject, string name = null, bool allowNull = false) + public virtual async Task> GetErrorsAsync(object validatingObject, string name = null, bool allowNull = false) { if (validatingObject == null) { @@ -58,7 +59,7 @@ namespace Volo.Abp.Validation { var contributor = (IObjectValidationContributor) scope.ServiceProvider.GetRequiredService(contributorType); - contributor.AddErrors(context); + await contributor.AddErrorsAsync(context); } } diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs index b4ce642471..0a0f4fcf5f 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs @@ -15,13 +15,13 @@ namespace Volo.Abp.Validation public override async Task InterceptAsync(IAbpMethodInvocation invocation) { - Validate(invocation); + await ValidateAsync(invocation); await invocation.ProceedAsync(); } - protected virtual void Validate(IAbpMethodInvocation invocation) + protected virtual async Task ValidateAsync(IAbpMethodInvocation invocation) { - _methodInvocationValidator.Validate( + await _methodInvocationValidator.ValidateAsync( new MethodInvocationValidationContext( invocation.TargetObject, invocation.Method, diff --git a/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs b/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs index a93eabda1d..6263d03ddd 100644 --- a/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs +++ b/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs @@ -39,7 +39,8 @@ namespace Volo.Abp.FluentValidation }, MyMethodInput3 = new MyMethodInput3 { - MyStringValue3 = "ccc" + MyStringValue3 = "ccc", + MyBoolValue3 = true } }); @@ -62,12 +63,13 @@ namespace Volo.Abp.FluentValidation }, MyMethodInput3 = new MyMethodInput3 { - MyStringValue3 = "c" + MyStringValue3 = "c", + MyBoolValue3 = false } } ) ); - + exception.ValidationErrors.ShouldContain(x => x.MemberNames.Contains("MyStringValue")); exception.ValidationErrors.ShouldContain(x => x.MemberNames.Contains("MyMethodInput2.MyStringValue2")); exception.ValidationErrors.ShouldContain(x => x.MemberNames.Contains("MyMethodInput3.MyStringValue3")); @@ -100,7 +102,7 @@ namespace Volo.Abp.FluentValidation output.ShouldBe("444"); } - + [DependsOn(typeof(AbpAutofacModule))] [DependsOn(typeof(AbpFluentValidationModule))] public class TestModule : AbpModule @@ -162,6 +164,8 @@ namespace Volo.Abp.FluentValidation { public string MyStringValue3 { get; set; } + + public bool MyBoolValue3 { get; set; } } public class MyMethodInput4 @@ -175,7 +179,8 @@ namespace Volo.Abp.FluentValidation { RuleFor(x => x.MyStringValue).Equal("aaa"); RuleFor(x => x.MyMethodInput2.MyStringValue2).Equal("bbb"); - RuleFor(customer => customer.MyMethodInput3).SetValidator(new MyMethodInput3Validator()); + RuleFor(x => x.MyMethodInput3).SetValidator(new MyMethodInput3Validator()); + RuleFor(x => x.MyMethodInput3).SetValidator(new MyMethodInput3AsyncValidator()); } } @@ -194,5 +199,15 @@ namespace Volo.Abp.FluentValidation RuleFor(x => x.MyStringValue3).Equal("ccc"); } } + + public class MyMethodInput3AsyncValidator : MethodInputBaseValidator + { + public MyMethodInput3AsyncValidator() + { + RuleFor(x => x.MyStringValue3).Equal("ccc"); + + RuleFor(x => x.MyBoolValue3).MustAsync((myBookValue3, cancellation) => Task.FromResult(myBookValue3)); + } + } } -} \ No newline at end of file +} From eefbb4a1e3c00207be89d22bfac105162603edcf Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 9 Jul 2021 15:35:06 +0800 Subject: [PATCH 02/14] Update Validation.md --- docs/en/Validation.md | 7 ++++--- docs/zh-Hans/Validation.md | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/en/Validation.md b/docs/en/Validation.md index c4821adf65..1d24c5e2a6 100644 --- a/docs/en/Validation.md +++ b/docs/en/Validation.md @@ -149,8 +149,8 @@ Once ABP determines a validation error, it throws an exception of type `AbpValid In addition to the automatic validation, you may want to manually validate an object. In this case, [inject](Dependency-Injection.md) and use the `IObjectValidator` service: -* `Validate` method validates the given object based on the validation rules and throws an `AbpValidationException` if it is not in a valid state. -* `GetErrors` doesn't throw an exception, but only returns the validation errors. +* `ValidateAsync` method validates the given object based on the validation rules and throws an `AbpValidationException` if it is not in a valid state. +* `GetErrorsAsync` doesn't throw an exception, but only returns the validation errors. `IObjectValidator` is implemented by the `ObjectValidator` by default. `ObjectValidator` is extensible; you can implement `IObjectValidationContributor` interface to contribute a custom logic. Example: @@ -158,13 +158,14 @@ In addition to the automatic validation, you may want to manually validate an ob public class MyObjectValidationContributor : IObjectValidationContributor, ITransientDependency { - public void AddErrors(ObjectValidationContext context) + public Task AddErrorsAsync(ObjectValidationContext context) { //Get the validating object var obj = context.ValidatingObject; //Add the validation errors if available context.Errors.Add(...); + return Task.CompletedTask; } } ```` diff --git a/docs/zh-Hans/Validation.md b/docs/zh-Hans/Validation.md index f6b2daf31f..6e46b07aed 100644 --- a/docs/zh-Hans/Validation.md +++ b/docs/zh-Hans/Validation.md @@ -130,8 +130,8 @@ namespace Acme.BookStore 除了自动验证你可能需要手动验证对象,这种情况下[注入](Dependency-Injection.md)并使用 `IObjectValidator` 服务: -* `Validate` 方法根据验证​​规则验证给定对象,如果对象没有被验证通过会抛出 `AbpValidationException` 异常. -* `GetErrors` 不会抛出异常,只返回验证错误. +* `ValidateAsync` 方法根据验证​​规则验证给定对象,如果对象没有被验证通过会抛出 `AbpValidationException` 异常. +* `GetErrorsAsync` 不会抛出异常,只返回验证错误. `IObjectValidator` 默认由 `ObjectValidator` 实现. `ObjectValidator`是可扩展的; 可以实现`IObjectValidationContributor`接口提供自定义逻辑. 示例 : @@ -140,13 +140,14 @@ namespace Acme.BookStore public class MyObjectValidationContributor : IObjectValidationContributor, ITransientDependency { - public void AddErrors(ObjectValidationContext context) + public Task AddErrorsAsync(ObjectValidationContext context) { //Get the validating object var obj = context.ValidatingObject; //Add the validation errors if available context.Errors.Add(...); + return Task.CompletedTask; } } ```` From e8fde2d472d9bb609a6757748d908dbd7bbebae1 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Wed, 28 Jul 2021 12:20:38 +0300 Subject: [PATCH 03/14] Remove abppkg.json files on build resolves https://github.com/abpframework/abp/issues/9585 --- common.props | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/common.props b/common.props index d7d4fd2a05..fef0ae48da 100644 --- a/common.props +++ b/common.props @@ -19,4 +19,13 @@ runtime; build; native; contentfiles; analyzers + + + + + true + Never + + + From cc3fc18adbbbd508f1741869088be54dff9cedf1 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 29 Jul 2021 10:05:54 +0800 Subject: [PATCH 04/14] Use `CancellationToken` if it exists in the parameter. --- .../DynamicProxying/DynamicHttpProxyInterceptor.cs | 7 ++++--- .../Http/DynamicProxying/IRegularTestController.cs | 3 +++ .../Http/DynamicProxying/RegularTestController.cs | 10 ++++++++-- .../RegularTestControllerClientProxy_Tests.cs | 13 +++++++++++-- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index b94a5a339d..b045e4bebd 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -174,7 +174,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying var response = await client.SendAsync( requestMessage, HttpCompletionOption.ResponseHeadersRead /*this will buffer only the headers, the content will be used as a stream*/, - GetCancellationToken() + GetCancellationToken(invocation) ); if (!response.IsSuccessStatusCode) @@ -306,9 +306,10 @@ namespace Volo.Abp.Http.Client.DynamicProxying return input; } - protected virtual CancellationToken GetCancellationToken() + protected virtual CancellationToken GetCancellationToken(IAbpMethodInvocation invocation) { - return CancellationTokenProvider.Token; + var cancellationToken = invocation.Arguments.LastOrDefault(x => x is CancellationToken); + return (CancellationToken?)cancellationToken ?? CancellationTokenProvider.Token; } } } diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs index 23694e4e72..15419539a2 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; namespace Volo.Abp.Http.DynamicProxying @@ -36,5 +37,7 @@ namespace Volo.Abp.Http.DynamicProxying Task PatchValueWithHeaderAndQueryStringAsync(string headerValue, string qsValue); Task DeleteByIdAsync(int id); + + Task AbortRequestAsync(CancellationToken cancellationToken); } } diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs index e18c2f8f4a..cfd13158f1 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Volo.Abp.Application.Services; using Volo.Abp.AspNetCore.Mvc; -using Volo.Abp.UI; namespace Volo.Abp.Http.DynamicProxying { @@ -129,6 +128,13 @@ namespace Volo.Abp.Http.DynamicProxying { return Task.FromResult(id + 1); } + + [HttpGet] + [Route("abort-request")] + public async Task AbortRequestAsync(CancellationToken cancellationToken) + { + await Task.Delay(100, cancellationToken); + } } public class Car diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs index bd35016989..bc179193a2 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs @@ -1,10 +1,10 @@ using System; +using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Localization; using Shouldly; using Volo.Abp.Http.Client; -using Volo.Abp.Http.Localization; using Volo.Abp.Localization; using Xunit; @@ -159,5 +159,14 @@ namespace Volo.Abp.Http.DynamicProxying (await _controller.DeleteByIdAsync(42)).ShouldBe(43); } + [Fact] + public async Task AbortRequestAsync() + { + var cts = new CancellationTokenSource(); + cts.CancelAfter(10); + + var exception = await Assert.ThrowsAsync(async () => await _controller.AbortRequestAsync(cts.Token)); + exception.InnerException.InnerException.Message.ShouldBe("The client aborted the request."); + } } } From a5dc1681fe319dccfbc067adc2dd081ad662ab10 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 29 Jul 2021 10:26:35 +0800 Subject: [PATCH 05/14] Skip the default CancellationToken. --- .../DynamicProxying/DynamicHttpProxyInterceptor.cs | 13 +++++++++++-- .../Http/DynamicProxying/IRegularTestController.cs | 2 +- .../Http/DynamicProxying/RegularTestController.cs | 3 ++- .../RegularTestControllerClientProxy_Tests.cs | 3 +++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index b045e4bebd..df890812bb 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -308,8 +308,17 @@ namespace Volo.Abp.Http.Client.DynamicProxying protected virtual CancellationToken GetCancellationToken(IAbpMethodInvocation invocation) { - var cancellationToken = invocation.Arguments.LastOrDefault(x => x is CancellationToken); - return (CancellationToken?)cancellationToken ?? CancellationTokenProvider.Token; + var cancellationTokenArg = invocation.Arguments.LastOrDefault(x => x is CancellationToken); + if (cancellationTokenArg != null) + { + var cancellationToken = (CancellationToken) cancellationTokenArg; + if (cancellationToken != default) + { + return cancellationToken; + } + } + + return CancellationTokenProvider.Token; } } } diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs index 15419539a2..d1c1275db7 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs @@ -38,6 +38,6 @@ namespace Volo.Abp.Http.DynamicProxying Task DeleteByIdAsync(int id); - Task AbortRequestAsync(CancellationToken cancellationToken); + Task AbortRequestAsync(CancellationToken cancellationToken = default); } } diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs index cfd13158f1..782b839626 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs @@ -131,9 +131,10 @@ namespace Volo.Abp.Http.DynamicProxying [HttpGet] [Route("abort-request")] - public async Task AbortRequestAsync(CancellationToken cancellationToken) + public async Task AbortRequestAsync(CancellationToken cancellationToken = default) { await Task.Delay(100, cancellationToken); + return "AbortRequestAsync"; } } diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs index bc179193a2..8df3e94b3c 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs @@ -165,6 +165,9 @@ namespace Volo.Abp.Http.DynamicProxying var cts = new CancellationTokenSource(); cts.CancelAfter(10); + var result = await _controller.AbortRequestAsync(default); + result.ShouldBe("AbortRequestAsync"); + var exception = await Assert.ThrowsAsync(async () => await _controller.AbortRequestAsync(cts.Token)); exception.InnerException.InnerException.Message.ShouldBe("The client aborted the request."); } From bbb003ffca9cbc3bd482ffc25784f24c3f000b93 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Thu, 29 Jul 2021 09:30:33 +0300 Subject: [PATCH 06/14] CLI: fix add-module command --- .../Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs index 273f011f41..62d490d64d 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs @@ -576,11 +576,12 @@ namespace Volo.Abp.Cli.ProjectModification return; } - var dbMigrationsProject = projectFiles.FirstOrDefault(p => p.EndsWith(".DbMigrations.csproj")); + var dbMigrationsProject = projectFiles.FirstOrDefault(p => p.EndsWith(".DbMigrations.csproj")) + ?? projectFiles.FirstOrDefault(p => p.EndsWith(".EntityFrameworkCore.csproj")) ; if (dbMigrationsProject == null) { - Logger.LogDebug("Solution doesn't have a \".DbMigrations\" project."); + Logger.LogDebug("Solution doesn't have a Migrations project."); if (!skipDbMigrations) { From 8f7914037951d3d1e501866c86a2701889f2ae21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87otur?= Date: Thu, 29 Jul 2021 16:05:42 +0300 Subject: [PATCH 07/14] Update Swagger.md --- docs/en/Swagger.md | 141 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 2 deletions(-) diff --git a/docs/en/Swagger.md b/docs/en/Swagger.md index 1fda8a0cd7..48ce252940 100644 --- a/docs/en/Swagger.md +++ b/docs/en/Swagger.md @@ -1,3 +1,140 @@ -# Swagger UI Integration +# Swagger Integration -TODO \ No newline at end of file +[Swagger (OpenAPI)](https://swagger.io/) is a language-agnostic specification for describing REST APIs. It allows both computers and humans to understand the capabilities of a REST API without direct access to the source code. Its main goals are to: + +- Minimize the amount of work needed to connect decoupled services. +- Reduce the amount of time needed to accurately document a service. + +ABP Framework offers a prebuilt module for full Swagger integration with small configurations. + +## Installation + +> This package is already installed by default with the startup template. So, most of the time, you don't need to install it manually. + +If installation is needed, it is suggested to use the [ABP CLI](CLI.md) to install this package. + +### Using the ABP CLI + +Open a command line window in the folder of the `Web` or `HttpApi.Host` project (.csproj file) and type the following command: + +```bash +abp add-package Volo.Abp.Swashbuckle +``` + +### Manual Installation + +If you want to manually install; + +1. Add the [Volo.Abp.Swashbuckle](https://www.nuget.org/packages/Volo.Abp.Swashbuckle) NuGet package to your `Web` or `HttpApi.Host` project: + + `Install-Package Volo.Abp.Swashbuckle` + +2. Add the `AbpSwashbuckleModule` to the dependency list of your module: + + ```csharp + [DependsOn( + //...other dependencies + typeof(AbpSwashbuckleModule) // <-- Add module dependency like that + )] + public class YourModule : AbpModule + { + } + ``` + +## Configuration + +First, we need to use `AddAbpSwaggerGen` extension to configure Swagger in `ConfigureServices` method of our module. + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + var services = contex.Services; + + //... other configarations. + + services.AddAbpSwaggerGen( + options => + { + options.SwaggerDoc("v1", new OpenApiInfo { Title = "Test API", Version = "v1" }); + options.DocInclusionPredicate((docName, description) => true); + options.CustomSchemaIds(type => type.FullName); + } + ); +} +``` + +Then we can use Swagger UI by calling `UseAbpSwaggerUI` method in the `OnApplicationInitialization` method of our module. + +```csharp +public override void OnApplicationInitialization(ApplicationInitializationContext context) +{ + var app = context.GetApplicationBuilder(); + + //... other configarations. + + app.UseAbpSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "Test API"); + }); + + //... other configarations. +} +``` + +## Using Swagger with OAUTH + +For non MVC/Tiered applications, we need to configure Swagger with OAUTH to handle authorization. + +> ABP Framework uses IdentityServer by default. To get more information about IDS, check this [documentation](Modules/IdentityServer.md). + + + +To do that, we need to use `AddAbpSwaggerGenWithOAuth` extension to configure Swagger with OAuth issuer and scopes in `ConfigureServices` method of our module. + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + var services = contex.Services; + + //... other configarations. + + services.AddAbpSwaggerGenWithOAuth( + "https://localhost:44341", // authority issuer + new Dictionary // + { // scopes + {"Test", "Test API"} // + }, // + options => + { + options.SwaggerDoc("v1", new OpenApiInfo { Title = "Test API", Version = "v1" }); + options.DocInclusionPredicate((docName, description) => true); + options.CustomSchemaIds(type => type.FullName); + } + ); +} +``` + +Then we can use Swagger UI by calling `UseAbpSwaggerUI` method in the `OnApplicationInitialization` method of our module. + +> Do not forget to set `OAuthClientId` and `OAuthClientSecret`. + + +```csharp +public override void OnApplicationInitialization(ApplicationInitializationContext context) +{ + var app = context.GetApplicationBuilder(); + + //... other configarations. + + app.UseAbpSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "Test API"); + + var configuration = context.ServiceProvider.GetRequiredService(); + options.OAuthClientId("Test_Swagger"); // clientId + options.OAuthClientSecret("1q2w3e*"); // clientSecret + }); + + //... other configarations. +} +``` \ No newline at end of file From 69fdb530a25ab1f018aec45b3aa161d3274ce488 Mon Sep 17 00:00:00 2001 From: Berkan Sasmaz Date: Fri, 30 Jul 2021 16:24:29 +0300 Subject: [PATCH 08/14] feat(Volo.Abp.HangFire): Add dashboard authorization for Hangfire --- .../Volo.Abp.HangFire.csproj | 1 + .../AbpHangfireAuthorizationFilter.cs | 46 +++++++++++++++++++ .../Volo/Abp/Hangfire/AbpHangfireModule.cs | 2 + 3 files changed, 49 insertions(+) create mode 100644 framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireAuthorizationFilter.cs diff --git a/framework/src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj b/framework/src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj index 4ec9d0a3b5..6b598d0edd 100644 --- a/framework/src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj +++ b/framework/src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj @@ -19,6 +19,7 @@ + diff --git a/framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireAuthorizationFilter.cs b/framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireAuthorizationFilter.cs new file mode 100644 index 0000000000..15fbb4f60c --- /dev/null +++ b/framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireAuthorizationFilter.cs @@ -0,0 +1,46 @@ +using System; +using System.Threading.Tasks; +using Hangfire.Dashboard; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Users; + +namespace Volo.Abp.Hangfire +{ + public class AbpHangfireAuthorizationFilter : IDashboardAsyncAuthorizationFilter + { + private readonly string _requiredPermissionName; + + public AbpHangfireAuthorizationFilter(string requiredPermissionName = null) + { + _requiredPermissionName = requiredPermissionName; + } + + public async Task AuthorizeAsync(DashboardContext context) + { + if (!IsLoggedIn(context)) + { + return false; + } + + if (_requiredPermissionName.IsNullOrEmpty()) + { + return true; + } + + return await IsPermissionGrantedAsync(context, _requiredPermissionName); + } + + private static bool IsLoggedIn(DashboardContext context) + { + var currentUser = context.GetHttpContext().RequestServices.GetRequiredService(); + return currentUser.IsAuthenticated; + } + + private static async Task IsPermissionGrantedAsync(DashboardContext context, string requiredPermissionName) + { + var permissionChecker = context.GetHttpContext().RequestServices.GetRequiredService(); + return await permissionChecker.IsGrantedAsync(requiredPermissionName); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireModule.cs b/framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireModule.cs index d69edb0b5d..1628d2f37f 100644 --- a/framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireModule.cs +++ b/framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireModule.cs @@ -1,10 +1,12 @@ using Hangfire; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Volo.Abp.Authorization; using Volo.Abp.Modularity; namespace Volo.Abp.Hangfire { + [DependsOn(typeof(AbpAuthorizationAbstractionsModule))] public class AbpHangfireModule : AbpModule { private BackgroundJobServer _backgroundJobServer; From 1f5b6046887be689f2b71e67110515586f5c4d2f Mon Sep 17 00:00:00 2001 From: Berkan Sasmaz Date: Fri, 30 Jul 2021 16:36:02 +0300 Subject: [PATCH 09/14] docs: update Background-Jobs-Hangfire document for dashboard authorization feature --- docs/en/Background-Jobs-Hangfire.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/en/Background-Jobs-Hangfire.md b/docs/en/Background-Jobs-Hangfire.md index cfa2e12a6b..6414f81c0e 100644 --- a/docs/en/Background-Jobs-Hangfire.md +++ b/docs/en/Background-Jobs-Hangfire.md @@ -79,3 +79,31 @@ After you have installed these NuGet packages, you need to configure your projec } ```` + +### Dashboard Authorization + +Hangfire can show a **dashboard page** so you can see the status of all background +jobs in real time. You can configure it as described in its +[documentation](http://docs.hangfire.io/en/latest/configuration/using-dashboard.html). +By default, this dashboard page is available for all users, and is not +authorized. You can integrate it in to ABP's [authorization +system](Authorization.md) using the **AbpHangfireAuthorizationFilter** +class defined in the Abp.HangFire package. Example configuration: + + app.UseHangfireDashboard("/hangfire", new DashboardOptions + { + AsyncAuthorization = new[] { new AbpHangfireAuthorizationFilter() } + }); + +This checks if the current user has logged in to the application. If you +want to require an additional permission, you can pass into its +constructor: + + app.UseHangfireDashboard("/hangfire", new DashboardOptions + { + AsyncAuthorization = new[] { new AbpHangfireAuthorizationFilter("MyHangFireDashboardPermissionName") } + }); + +**Note**: UseHangfireDashboard should be called after the authentication +middleware in your Startup class (probably as the last line). Otherwise, +authorization will always fail. From 956a6aed6305bde218d0ed7d7d245e4593695f2a Mon Sep 17 00:00:00 2001 From: Berkan Sasmaz Date: Fri, 30 Jul 2021 16:44:50 +0300 Subject: [PATCH 10/14] Update Background-Jobs-Hangfire.md --- docs/en/Background-Jobs-Hangfire.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Background-Jobs-Hangfire.md b/docs/en/Background-Jobs-Hangfire.md index 6414f81c0e..952dc62880 100644 --- a/docs/en/Background-Jobs-Hangfire.md +++ b/docs/en/Background-Jobs-Hangfire.md @@ -88,7 +88,7 @@ jobs in real time. You can configure it as described in its By default, this dashboard page is available for all users, and is not authorized. You can integrate it in to ABP's [authorization system](Authorization.md) using the **AbpHangfireAuthorizationFilter** -class defined in the Abp.HangFire package. Example configuration: +class defined in the Volo.Abp.Hangfire package. Example configuration: app.UseHangfireDashboard("/hangfire", new DashboardOptions { From d9a2e41a7b14d39e4395879f798d188c12f6a808 Mon Sep 17 00:00:00 2001 From: ebicoglu Date: Sat, 31 Jul 2021 20:03:29 +0300 Subject: [PATCH 11/14] Update Background-Jobs-Hangfire.md --- docs/en/Background-Jobs-Hangfire.md | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/docs/en/Background-Jobs-Hangfire.md b/docs/en/Background-Jobs-Hangfire.md index 952dc62880..a690d9d17b 100644 --- a/docs/en/Background-Jobs-Hangfire.md +++ b/docs/en/Background-Jobs-Hangfire.md @@ -82,28 +82,23 @@ After you have installed these NuGet packages, you need to configure your projec ### Dashboard Authorization -Hangfire can show a **dashboard page** so you can see the status of all background -jobs in real time. You can configure it as described in its -[documentation](http://docs.hangfire.io/en/latest/configuration/using-dashboard.html). -By default, this dashboard page is available for all users, and is not -authorized. You can integrate it in to ABP's [authorization -system](Authorization.md) using the **AbpHangfireAuthorizationFilter** -class defined in the Volo.Abp.Hangfire package. Example configuration: +Hangfire Dashboard provides information about your background jobs, including method names and serialized arguments as well as gives you an opportunity to manage them by performing different actions – retry, delete, trigger, etc. So it is important to restrict access to the Dashboard. +To make it secure by default, only local requests are allowed, however you can change this by following the [official documentation](http://docs.hangfire.io/en/latest/configuration/using-dashboard.html) of Hangfire. + +You can integrate the Hangfire dashboard to [ABP authorization system](Authorization.md) using the **AbpHangfireAuthorizationFilter** +class. This class is defined in the `Volo.Abp.Hangfire` package. The following example, checks if the current user is logged in to the application: app.UseHangfireDashboard("/hangfire", new DashboardOptions { AsyncAuthorization = new[] { new AbpHangfireAuthorizationFilter() } }); -This checks if the current user has logged in to the application. If you -want to require an additional permission, you can pass into its -constructor: +If you want to require an additional permission, you can pass it into the constructor as below: app.UseHangfireDashboard("/hangfire", new DashboardOptions { AsyncAuthorization = new[] { new AbpHangfireAuthorizationFilter("MyHangFireDashboardPermissionName") } }); -**Note**: UseHangfireDashboard should be called after the authentication -middleware in your Startup class (probably as the last line). Otherwise, -authorization will always fail. +**Important**: `UseHangfireDashboard` should be called after the authentication middleware in your `Startup` class (probably at the last line). Otherwise, +authorization will always fail! From 0bd08ac67b13cc39400c6d89c7e60775281eb4e6 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Sun, 1 Aug 2021 10:54:23 +0800 Subject: [PATCH 12/14] Fix NU1701 warning of Volo.Abp.Sms.Aliyun --- framework/src/Volo.Abp.Sms.Aliyun/Volo.Abp.Sms.Aliyun.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/src/Volo.Abp.Sms.Aliyun/Volo.Abp.Sms.Aliyun.csproj b/framework/src/Volo.Abp.Sms.Aliyun/Volo.Abp.Sms.Aliyun.csproj index cae6301daf..83cea09848 100644 --- a/framework/src/Volo.Abp.Sms.Aliyun/Volo.Abp.Sms.Aliyun.csproj +++ b/framework/src/Volo.Abp.Sms.Aliyun/Volo.Abp.Sms.Aliyun.csproj @@ -18,6 +18,7 @@ + From 327e3425977d5eba640253b826806c2feaa40d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87otur?= Date: Mon, 2 Aug 2021 00:16:44 +0300 Subject: [PATCH 13/14] Update docs navigation for swagger integration doc. --- docs/en/{Swagger.md => API/Swagger-Integration.md} | 0 docs/en/docs-nav.json | 4 ++++ 2 files changed, 4 insertions(+) rename docs/en/{Swagger.md => API/Swagger-Integration.md} (100%) diff --git a/docs/en/Swagger.md b/docs/en/API/Swagger-Integration.md similarity index 100% rename from docs/en/Swagger.md rename to docs/en/API/Swagger-Integration.md diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 5a2b18d365..398da988e4 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -578,6 +578,10 @@ "path": "API/Application-Configuration.md" } ] + }, + { + "text": "Swagger Integration", + "path": "API/Swagger-Integration.md" } ] }, From 9c2372ec46e56f288cd068adee32114265dc5790 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Mon, 2 Aug 2021 13:05:52 +0800 Subject: [PATCH 14/14] Add GetListAsync mehtod to IDocumentRepository --- .../Volo/Docs/Documents/IDocumentRepository.cs | 8 +++++++- .../Volo/Docs/Documents/EFCoreDocumentRepository.cs | 9 +++++++++ .../Volo/Docs/Documents/MongoDocumentRepository.cs | 10 ++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs index 6abd112533..6b911c6e36 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs @@ -25,6 +25,12 @@ namespace Volo.Docs.Documents string version, CancellationToken cancellationToken = default); + Task> GetListAsync( + Guid? projectId, + string version, + string name, + CancellationToken cancellationToken = default); + Task> GetAllAsync( Guid? projectId, string name, @@ -67,4 +73,4 @@ namespace Volo.Docs.Documents Task GetAsync(Guid id, CancellationToken cancellationToken = default); } -} \ No newline at end of file +} diff --git a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs index e44c52c61c..a322120c3d 100644 --- a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs +++ b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs @@ -38,6 +38,15 @@ namespace Volo.Docs.Documents return await (await GetDbSetAsync()).Where(d => d.ProjectId == projectId).ToListAsync(cancellationToken: cancellationToken); } + public async Task> GetListAsync(Guid? projectId, string version, string name, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .WhereIf(version != null, x => x.Version == version) + .WhereIf(name != null, x => x.Name == name) + .WhereIf(projectId.HasValue, x => x.ProjectId == projectId) + .ToListAsync(cancellationToken: cancellationToken); + } + public async Task> GetAllAsync( Guid? projectId, string name, diff --git a/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs b/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs index 955319d848..3a57330557 100644 --- a/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs +++ b/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs @@ -57,6 +57,16 @@ namespace Volo.Docs.Documents x.Version == version, cancellationToken: cancellationToken); } + public async Task> GetListAsync(Guid? projectId, string version, string name, CancellationToken cancellationToken = default) + { + return await (await GetMongoQueryableAsync(cancellationToken)) + .WhereIf(version != null, x => x.Version == version) + .WhereIf(name != null, x => x.Name == name) + .WhereIf(projectId.HasValue, x => x.ProjectId == projectId) + .As>() + .ToListAsync(cancellationToken); + } + public async Task> GetAllAsync( Guid? projectId, string name,