mirror of https://github.com/abpframework/abp.git
161 changed files with 1801 additions and 896 deletions
|
After Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 24 KiB |
@ -1,3 +1,182 @@ |
|||||
## Validation |
# Validação |
||||
|
|
||||
Façam |
O sistema de validação é utilizado para validar a entrada do usuário ou a requisição do cliente para uma ação de um controller ou por um serviço. |
||||
|
|
||||
|
O ABP é compatível com o sistema de Validação de Modelos do ASP.NET Core e tudo escrito na [sua documentação](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation) é válido para aplicações baseadas no ABP. Logo, esse documento foca nas funcionalidades do ABP ao invés de repetir a documentação da Microsoft. |
||||
|
|
||||
|
Além disso, o ABP adiciona os seguintes benefícios: |
||||
|
|
||||
|
* Define `IValidationEnabled` para adicionar validação automática para uma classe qualquer. Como todos os [serviços de aplicação](Application-Services.md) já o implementam, eles também são validados automaticamente. |
||||
|
* Automaticamente traduz os erros de validação para os atributos de anotação de dados. |
||||
|
* Provê serviços extensíveis para validar a chamada de um método ou o estado de um objeto. |
||||
|
* Provê integração com o [FluentValidation](https://fluentvalidation.net/) |
||||
|
|
||||
|
## Validando DTOs |
||||
|
|
||||
|
Essa seção introduz brevemente o sistema de validação. Para mais detalhes, veja a [Documentação da Validação de Modelo em ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation). |
||||
|
|
||||
|
### Atributos de anotação de dados |
||||
|
|
||||
|
Utilizar anotações de dados é uma maneira simples de implementar uma validação formal para um [DTO](Data-Transfer-Objects.md) de uma forma declarativa. Exemplo: |
||||
|
|
||||
|
````csharp |
||||
|
public class CreateBookDto |
||||
|
{ |
||||
|
[Required] |
||||
|
[StringLength(100)] |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[StringLength(1000)] |
||||
|
public string Description { get; set; } |
||||
|
|
||||
|
[Range(0, 999.99)] |
||||
|
public decimal Price { get; set; } |
||||
|
} |
||||
|
```` |
||||
|
Quando você utilizar essa classe como parâmetro para um [serviço da aplicação](Application-Services.md) ou um controller, ele será automaticamente validado e a validação traduzida será lançada ([e tratada](Exception-Handling.md) pelo ABP framework). |
||||
|
|
||||
|
### IValidatableObject |
||||
|
|
||||
|
`IValidatableObject` pode ser implementado por um DTO para executar uma lógica customizada de validação. O `CreateBookDto` no exemplo a seguir implementa essa interface e verifica se o `Name` é igual a `Description` e retorna um erro de validação nesse caso. |
||||
|
|
||||
|
````csharp |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
|
||||
|
namespace Acme.BookStore |
||||
|
{ |
||||
|
public class CreateBookDto : IValidatableObject |
||||
|
{ |
||||
|
[Required] |
||||
|
[StringLength(100)] |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[StringLength(1000)] |
||||
|
public string Description { get; set; } |
||||
|
|
||||
|
[Range(0, 999.99)] |
||||
|
public decimal Price { get; set; } |
||||
|
|
||||
|
public IEnumerable<ValidationResult> Validate( |
||||
|
ValidationContext validationContext) |
||||
|
{ |
||||
|
if (Name == Description) |
||||
|
{ |
||||
|
yield return new ValidationResult( |
||||
|
"Name and Description can not be the same!", |
||||
|
new[] { "Name", "Description" } |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
#### Resolvendo um serviço. |
||||
|
|
||||
|
Se você precisar resolver um serviço do [sistema de injeção de dependências](Dependency-Injection.md), você pode utilizar o objeto `ValidationContext`. |
||||
|
|
||||
|
````csharp |
||||
|
var myService = validationContext.GetRequiredService<IMyService>(); |
||||
|
```` |
||||
|
|
||||
|
> Enquanto resolver os serviços no método `Validate` permite várias possibilidades, não é um boa prática implementar sua lógica de validação do domínio nos DTOs. Mantenha os DTOs simples. Seu propósito é transferir dados (DTO: Data Transfer Object, ou Objeto de Transferência de Dados). |
||||
|
|
||||
|
## Infraestrutura de Validação. |
||||
|
|
||||
|
Essa seção explica alguns serviços adicionais fornecidos pelo ABP Framework. |
||||
|
|
||||
|
### Interface IValidationEnabled |
||||
|
|
||||
|
`IValidationEnabled` é um marcador vazio de interface que pode ser implementado por qualquer classe (registrada e resolvida a partir do [DI](Dependency-Injection.md)) para permitir que o ABP framework realize o sistema de validação para os métodos da classe. Por exemplo: |
||||
|
|
||||
|
````csharp |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace Acme.BookStore |
||||
|
{ |
||||
|
public class MyService : ITransientDependency, IValidationEnabled |
||||
|
{ |
||||
|
public virtual async Task DoItAsync(MyInput input) |
||||
|
{ |
||||
|
//... |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
> O ABP framework utiliza o sistema de [Proxying Dinâmico / Interceptadores](Dynamic-Proxying-Interceptors.md) para realizar a validação. Para fazê-lo funcionar, seu método deve ser **virtual** ou seu serviço deve ser injetado e utilizado através de uma **interface** (como `IMyService`). |
||||
|
|
||||
|
#### Habilitando e Desabilitando Validações |
||||
|
|
||||
|
Você pode utilizar o `[DisableValidation]` e desabilitar a validação para métodos, classes e propriedades. |
||||
|
|
||||
|
````csharp |
||||
|
[DisableValidation] |
||||
|
public Void MyMethod() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
[DisableValidation] |
||||
|
public class InputClass |
||||
|
{ |
||||
|
public string MyProperty { get; set; } |
||||
|
} |
||||
|
|
||||
|
public class InputClass |
||||
|
{ |
||||
|
[DisableValidation] |
||||
|
public string MyProperty { get; set; } |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
### AbpValidationException |
||||
|
|
||||
|
Uma vez que o ABP determina um erro de validação, é lançada uma validação do tipo `AbpValidationException`. O código da sua aplicação poderá lançar o `AbpValidationException`, mas na maioria das vezes não será necessário. |
||||
|
|
||||
|
* A propriedade `ValidationErrors` do `AbpValidationException` contem a lista com os erros de validação. |
||||
|
* O nível de log do `AbpValidationException` é definido como `Warning`. Todos os erros de validação são logados no [Sistema de Logging](Logging.md). |
||||
|
* `AbpValidationException` é tratado automaticamente pelo ABP framework e é convertido em um erro utilizável com o código de status HTTP 400. Veja a documentação de [Manipulação de Exceção](Exception-Handling.md) para mais informações. |
||||
|
|
||||
|
## Tópicos Avançados |
||||
|
|
||||
|
### IObjectValidator |
||||
|
|
||||
|
Além da validação automática, você pode querer validar um objeto manualmente. Nesse caso, [injete](Dependency-Injection.md) e use o serviço `IObjectValidator`: |
||||
|
|
||||
|
* O método `ValidateAsync` valida o objeto informado baseado nas regras de validação e lança uma `AbpValidationException` se não estiver em um estado válido. |
||||
|
|
||||
|
* `GetErrorsAsync` não lança uma exceção, somente retorna os erros de validação. |
||||
|
|
||||
|
`IObjectValidator` é implementado pelo `ObjectValidator` por padrão. `ObjectValidator` é extensível; você pode implementar a interface `IObjectValidationContributor` para contribuir com uma lógica customizada. Exemplo: |
||||
|
|
||||
|
````csharp |
||||
|
public class MyObjectValidationContributor |
||||
|
: IObjectValidationContributor, ITransientDependency |
||||
|
{ |
||||
|
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; |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
* Lembre-se de registrar sua classe no [DI](Dependency-Injection.md) (implementar `ITransientDependency` faz isso no exemplo anterior) |
||||
|
* ABP vai automaticamente descobrir sua classe e utilizá-la em qualquer tipo de validação de objetos (incluindo chamadas de métodos de validação automáticas). |
||||
|
|
||||
|
### IMethodInvocationValidator |
||||
|
|
||||
|
`IMethodInvocationValidator` é utilizado para validar a chamada de um método. Ele utiliza internamente o `IObjectValidator` para validar os objetos passados na chamada do método. Você normalmente não precisa deste serviço, já que ele é utilizado automaticamente pelo framework, mas você pode querer reutilizar ou substituir na sua aplicação em alguns casos raros. |
||||
|
|
||||
|
## Integração com FluentValidation |
||||
|
|
||||
|
O pacote Volo.Abp.FluentValidation integra a biblioteca FluentValidation com o sistema de validação (implementando o `IObjectValidationContributor`). Veja o [documento de Integração com o FluentValidation](FluentValidation.md) para mais informações. |
||||
|
|||||
@ -0,0 +1,7 @@ |
|||||
|
@if (LayoutHookViewModel.Hooks.Any()) |
||||
|
{ |
||||
|
foreach (var hook in LayoutHookViewModel.Hooks) |
||||
|
{ |
||||
|
<DynamicComponent Type="@hook.ComponentType" /> |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Components; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.Ui.LayoutHooks; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Components.Web.Theming.Components.LayoutHooks; |
||||
|
|
||||
|
public partial class LayoutHook : ComponentBase |
||||
|
{ |
||||
|
[Parameter] |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
[Parameter] |
||||
|
public string Layout { get; set; } |
||||
|
|
||||
|
[Inject] |
||||
|
protected IOptions<AbpLayoutHookOptions> LayoutHookOptions { get; set; } |
||||
|
|
||||
|
protected LayoutHookViewModel LayoutHookViewModel { get; private set; } |
||||
|
|
||||
|
protected override Task OnInitializedAsync() |
||||
|
{ |
||||
|
if (LayoutHookOptions.Value.Hooks.TryGetValue(Name, out var layoutHooks)) |
||||
|
{ |
||||
|
layoutHooks = layoutHooks |
||||
|
.WhereIf(string.IsNullOrWhiteSpace(Layout), x => x.Layout == Layout) |
||||
|
.ToList(); |
||||
|
} |
||||
|
|
||||
|
layoutHooks ??= new List<LayoutHookInfo>(); |
||||
|
|
||||
|
LayoutHookViewModel = new LayoutHookViewModel(layoutHooks.ToArray(), Layout); |
||||
|
|
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace Volo.Abp.AspNetCore.Components.Web.Theming.Layout; |
||||
|
|
||||
|
public static class StandardLayouts |
||||
|
{ |
||||
|
public const string Application = "Application"; |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Reflection; |
||||
|
using Autofac.Core; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Autofac; |
||||
|
|
||||
|
public class AbpPropertySelector : DefaultPropertySelector |
||||
|
{ |
||||
|
public AbpPropertySelector(bool preserveSetValues) |
||||
|
: base(preserveSetValues) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override bool InjectProperty(PropertyInfo propertyInfo, object instance) |
||||
|
{ |
||||
|
return propertyInfo.GetCustomAttributes(typeof(DisablePropertyInjectionAttribute), true).IsNullOrEmpty() && |
||||
|
base.InjectProperty(propertyInfo, instance); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)] |
||||
|
public class DisablePropertyInjectionAttribute : Attribute |
||||
|
{ |
||||
|
|
||||
|
} |
||||
@ -1,7 +1,7 @@ |
|||||
using System; |
using System; |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
|
|
||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook; |
namespace Volo.Abp.Ui.LayoutHooks; |
||||
|
|
||||
public class AbpLayoutHookOptions |
public class AbpLayoutHookOptions |
||||
{ |
{ |
||||
@ -1,11 +1,11 @@ |
|||||
using System; |
using System; |
||||
|
|
||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook; |
namespace Volo.Abp.Ui.LayoutHooks; |
||||
|
|
||||
public class LayoutHookInfo |
public class LayoutHookInfo |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// ViewComponent type.
|
/// Component type.
|
||||
/// </summary>
|
/// </summary>
|
||||
public Type ComponentType { get; } |
public Type ComponentType { get; } |
||||
|
|
||||
@ -1,4 +1,4 @@ |
|||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook; |
namespace Volo.Abp.Ui.LayoutHooks; |
||||
|
|
||||
public class LayoutHookViewModel |
public class LayoutHookViewModel |
||||
{ |
{ |
||||
@ -1,4 +1,4 @@ |
|||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook; |
namespace Volo.Abp.Ui.LayoutHooks; |
||||
|
|
||||
public static class LayoutHooks |
public static class LayoutHooks |
||||
{ |
{ |
||||
@ -1,9 +0,0 @@ |
|||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.Application.Dtos; |
|
||||
using Volo.Abp.Application.Services; |
|
||||
|
|
||||
namespace Volo.CmsKit.Admin.Contents; |
|
||||
public interface IContentAdminAppService : IApplicationService |
|
||||
{ |
|
||||
Task<ListResultDto<ContentWidgetDto>> GetWidgetsAsync(); |
|
||||
} |
|
||||
@ -1,32 +0,0 @@ |
|||||
using System.Linq; |
|
||||
using System.Threading.Tasks; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using Volo.Abp.Application.Dtos; |
|
||||
using Volo.CmsKit.Contents; |
|
||||
|
|
||||
namespace Volo.CmsKit.Admin.Contents; |
|
||||
|
|
||||
public class ContentAdminAppService : CmsKitAdminAppServiceBase, IContentAdminAppService |
|
||||
{ |
|
||||
private readonly CmsKitContentWidgetOptions _options; |
|
||||
|
|
||||
public ContentAdminAppService(IOptions<CmsKitContentWidgetOptions> options) |
|
||||
{ |
|
||||
_options = options.Value; |
|
||||
} |
|
||||
|
|
||||
public virtual Task<ListResultDto<ContentWidgetDto>> GetWidgetsAsync() |
|
||||
{ |
|
||||
return Task.FromResult(new ListResultDto<ContentWidgetDto>() |
|
||||
{ |
|
||||
Items = _options.WidgetConfigs |
|
||||
.Select(n => |
|
||||
new ContentWidgetDto |
|
||||
{ |
|
||||
Key = n.Key, |
|
||||
Details = new WidgetDetailDto() { EditorComponentName = n.Value.EditorComponentName, Name = n.Value.Name }, |
|
||||
|
|
||||
}).ToList() |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
@ -1,22 +0,0 @@ |
|||||
// This file is automatically generated by ABP framework to use MVC Controllers from CSharp
|
|
||||
using System; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.Application.Dtos; |
|
||||
using Volo.Abp.Http.Client; |
|
||||
using Volo.Abp.Http.Modeling; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
using Volo.Abp.Http.Client.ClientProxying; |
|
||||
using Volo.CmsKit.Admin.Contents; |
|
||||
|
|
||||
// ReSharper disable once CheckNamespace
|
|
||||
namespace Volo.CmsKit.Admin.Contents.ClientProxies; |
|
||||
|
|
||||
[Dependency(ReplaceServices = true)] |
|
||||
[ExposeServices(typeof(IContentAdminAppService), typeof(ContentAdminClientProxy))] |
|
||||
public partial class ContentAdminClientProxy : ClientProxyBase<IContentAdminAppService>, IContentAdminAppService |
|
||||
{ |
|
||||
public virtual async Task<ListResultDto<ContentWidgetDto>> GetWidgetsAsync() |
|
||||
{ |
|
||||
return await RequestAsync<ListResultDto<ContentWidgetDto>>(nameof(GetWidgetsAsync)); |
|
||||
} |
|
||||
} |
|
||||
@ -1,7 +0,0 @@ |
|||||
// This file is part of ContentAdminClientProxy, you can customize it here
|
|
||||
// ReSharper disable once CheckNamespace
|
|
||||
namespace Volo.CmsKit.Admin.Contents.ClientProxies; |
|
||||
|
|
||||
public partial class ContentAdminClientProxy |
|
||||
{ |
|
||||
} |
|
||||
@ -1,25 +0,0 @@ |
|||||
using System.Threading.Tasks; |
|
||||
using Microsoft.AspNetCore.Mvc; |
|
||||
using Volo.Abp; |
|
||||
using Volo.Abp.Application.Dtos; |
|
||||
|
|
||||
namespace Volo.CmsKit.Admin.Contents; |
|
||||
|
|
||||
[RemoteService(Name = CmsKitAdminRemoteServiceConsts.RemoteServiceName)] |
|
||||
[Area(CmsKitAdminRemoteServiceConsts.ModuleName)] |
|
||||
[Route("api/cms-kit-admin/contents")] |
|
||||
public class ContentAdminController : CmsKitAdminController, IContentAdminAppService |
|
||||
{ |
|
||||
protected IContentAdminAppService ContentAdminAppService { get; } |
|
||||
|
|
||||
public ContentAdminController(IContentAdminAppService contentAdminAppService) |
|
||||
{ |
|
||||
ContentAdminAppService = contentAdminAppService; |
|
||||
} |
|
||||
|
|
||||
[HttpGet] |
|
||||
public virtual Task<ListResultDto<ContentWidgetDto>> GetWidgetsAsync() |
|
||||
{ |
|
||||
return ContentAdminAppService.GetWidgetsAsync(); |
|
||||
} |
|
||||
} |
|
||||
@ -1,10 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.Application.Services; |
|
||||
|
|
||||
namespace Volo.CmsKit.Contents; |
|
||||
|
|
||||
public interface IContentAppService : IApplicationService |
|
||||
{ |
|
||||
Task<List<ContentFragment>> ParseAsync(string content); |
|
||||
} |
|
||||
@ -1,22 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.GlobalFeatures; |
|
||||
using Volo.CmsKit.GlobalFeatures; |
|
||||
|
|
||||
namespace Volo.CmsKit.Contents; |
|
||||
|
|
||||
[RequiresGlobalFeature(typeof(PagesFeature))] |
|
||||
public class ContentAppService : CmsKitAppServiceBase, IContentAppService |
|
||||
{ |
|
||||
protected ContentParser ContentParser { get; } |
|
||||
|
|
||||
public ContentAppService(ContentParser contentParser) |
|
||||
{ |
|
||||
ContentParser = contentParser; |
|
||||
} |
|
||||
|
|
||||
public async Task<List<ContentFragment>> ParseAsync(string content) |
|
||||
{ |
|
||||
return await ContentParser.ParseAsync(content); |
|
||||
} |
|
||||
} |
|
||||
@ -1,6 +1,6 @@ |
|||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
|
|
||||
namespace Volo.CmsKit.Contents; |
namespace Volo.CmsKit.Web.Contents; |
||||
|
|
||||
public class CmsKitContentWidgetOptions |
public class CmsKitContentWidgetOptions |
||||
{ |
{ |
||||
@ -1,4 +1,4 @@ |
|||||
namespace Volo.CmsKit.Contents; |
namespace Volo.CmsKit.Web.Contents; |
||||
|
|
||||
public class ContentWidgetConfig |
public class ContentWidgetConfig |
||||
{ |
{ |
||||
@ -0,0 +1,28 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using AutoMapper; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.CmsKit.Contents; |
||||
|
using Volo.CmsKit.Users; |
||||
|
|
||||
|
namespace Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Blogs; |
||||
|
|
||||
|
[AutoMap(typeof(BlogPostCommonDto), ReverseMap = true)] |
||||
|
public class BlogPostViewModel : AuditedEntityDto<Guid> |
||||
|
{ |
||||
|
public Guid BlogId { get; set; } |
||||
|
|
||||
|
public string Title { get; set; } |
||||
|
|
||||
|
public string Slug { get; set; } |
||||
|
|
||||
|
public string ShortDescription { get; set; } |
||||
|
|
||||
|
public string Content { get; set; } |
||||
|
|
||||
|
public List<ContentFragment> ContentFragments { get; set; } |
||||
|
|
||||
|
public Guid? CoverImageMediaId { get; set; } |
||||
|
|
||||
|
public CmsUserDto Author { get; set; } |
||||
|
} |
||||
@ -1,27 +0,0 @@ |
|||||
using System.Linq; |
|
||||
using System.Threading.Tasks; |
|
||||
using Shouldly; |
|
||||
using Volo.CmsKit.Admin.Contents; |
|
||||
using Xunit; |
|
||||
|
|
||||
namespace Volo.CmsKit.Contents; |
|
||||
|
|
||||
public class ContentAdminAppService_Tests : CmsKitApplicationTestBase |
|
||||
{ |
|
||||
private readonly IContentAdminAppService _contentAdminAppService; |
|
||||
|
|
||||
public ContentAdminAppService_Tests() |
|
||||
{ |
|
||||
_contentAdminAppService = GetRequiredService<IContentAdminAppService>(); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task ShouldGet_PagedListAsync() |
|
||||
{ |
|
||||
var widgets = await _contentAdminAppService.GetWidgetsAsync(); |
|
||||
|
|
||||
widgets.Items.Count.ShouldBe(0); |
|
||||
widgets.Items.Any().ShouldBeFalse(); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -1,138 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using System.Threading.Tasks; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using Shouldly; |
|
||||
using Xunit; |
|
||||
|
|
||||
namespace Volo.CmsKit.Contents; |
|
||||
public class ContentParser_Test : CmsKitDomainTestBase |
|
||||
{ |
|
||||
private readonly CmsKitTestData testData; |
|
||||
private readonly IOptions<CmsKitContentWidgetOptions> _options; |
|
||||
private ContentParser contentParser; |
|
||||
|
|
||||
public ContentParser_Test() |
|
||||
{ |
|
||||
testData = GetRequiredService<CmsKitTestData>(); |
|
||||
_options = GetRequiredService<IOptions<CmsKitContentWidgetOptions>>(); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task ParseAsync_ShouldWorkWithDifferentWidgetTypes() |
|
||||
{ |
|
||||
_options.Value.AddWidget(testData.PollName, testData.WidgetName, string.Empty); |
|
||||
_options.Value.AddWidget("ImageGallery", "ImageGallery", string.Empty); |
|
||||
contentParser = new ContentParser(_options); |
|
||||
|
|
||||
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|
||||
[Widget Type=""Poll"" Code=""poll-name""] |
|
||||
Thanks _for_ *your * feedback. |
|
||||
[Widget GalleryName=""Xyz"" Type=""ImageGallery"" Source=""GoogleDrive""]";
|
|
||||
|
|
||||
var widgets = await contentParser.ParseAsync(content); |
|
||||
|
|
||||
widgets.ShouldNotBeNull(); |
|
||||
widgets.Count.ShouldBe(4); |
|
||||
widgets[1].ExtraProperties.Count.ShouldBe(2); |
|
||||
widgets[3].ExtraProperties.Count.ShouldBe(3); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task ParseAsync_ShouldWorkWithoutConfigOptions() |
|
||||
{ |
|
||||
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|
||||
[Widget Type= ""Poll"" Code =""poll-name""] |
|
||||
Thanks _for_ *your * feedback.";
|
|
||||
|
|
||||
contentParser = new ContentParser(_options); |
|
||||
var widgets = await contentParser.ParseAsync(content); |
|
||||
|
|
||||
widgets.ShouldNotBeNull(); |
|
||||
widgets.Count.ShouldBe(1);//Ignored Widget
|
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task ParseAsync_ShouldWorkWithWrongConfigOptions() |
|
||||
{ |
|
||||
_options.Value.AddWidget(testData.WidgetName, testData.PollName, string.Empty); |
|
||||
contentParser = new ContentParser(_options); |
|
||||
|
|
||||
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|
||||
[Widget Type= ""Poll"" Code =""poll-name""] |
|
||||
Thanks _for_ *your * feedback.";
|
|
||||
|
|
||||
var widgets = await contentParser.ParseAsync(content); |
|
||||
|
|
||||
widgets.ShouldNotBeNull(); |
|
||||
widgets.Count.ShouldBe(2); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task ParseAsync_ShouldWorkWithWrongWidgetType() |
|
||||
{ |
|
||||
_options.Value.AddWidget(testData.PollName, testData.WidgetName, string.Empty); |
|
||||
contentParser = new ContentParser(_options); |
|
||||
|
|
||||
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|
||||
[Widget Wrong Type= ""Poll"" Code =""poll-name""] |
|
||||
Thanks _for_ *your * feedback.";
|
|
||||
|
|
||||
var widgets = await contentParser.ParseAsync(content); |
|
||||
|
|
||||
widgets.ShouldNotBeNull(); |
|
||||
widgets.Count.ShouldBe(2); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task ParseAsync_ShouldWorkWithWrongPollName() |
|
||||
{ |
|
||||
_options.Value.AddWidget(testData.PollName, testData.WidgetName, string.Empty); |
|
||||
contentParser = new ContentParser(_options); |
|
||||
|
|
||||
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|
||||
[Widget Type= ""Poll"" PollWrongName =""poll-name""] |
|
||||
Thanks _for_ *your * feedback.";
|
|
||||
|
|
||||
var widgets = await contentParser.ParseAsync(content); |
|
||||
|
|
||||
widgets.ShouldNotBeNull(); |
|
||||
widgets.Count.ShouldBe(3); |
|
||||
} |
|
||||
|
|
||||
[Theory] |
|
||||
[MemberData(nameof(ExampleData))] |
|
||||
public async Task ParseAsync_ShouldWorkProperlyWithCorrectInputs(string content, int expectedLine) |
|
||||
{ |
|
||||
_options.Value.AddWidget(testData.PollName, testData.WidgetName, string.Empty); |
|
||||
contentParser = new ContentParser(_options); |
|
||||
|
|
||||
var widgets = await contentParser.ParseAsync(content); |
|
||||
|
|
||||
widgets.ShouldNotBeNull(); |
|
||||
widgets.Count.ShouldBe(expectedLine); |
|
||||
} |
|
||||
|
|
||||
public static IEnumerable<object[]> ExampleData => |
|
||||
new List<object[]> |
|
||||
{ |
|
||||
new object[] { @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|
||||
[Widget Type=""Poll"" Code=""poll-name""] |
|
||||
Thanks _for_ *your * feedback.", 3},
|
|
||||
|
|
||||
new object[] { @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|
||||
[Widget Type=""Poll"" Code=""poll-name""] |
|
||||
Thanks _for_ *your * feedback. |
|
||||
[Widget Type=""Poll"" Code=""poll-name1""]", 4 },
|
|
||||
|
|
||||
new object[] { @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|
||||
Thanks _for_ *your * feedback. |
|
||||
[Widget Type=""Poll"" Code=""poll-name""]", 2 },
|
|
||||
|
|
||||
new object[] { @"[Widget Type=""Poll"" Code=""poll-name""] gg [Widget Type=""Poll"" Code=""poll-name1""]**ABP Framework** is completely open source and developed in a community-driven manner.
|
|
||||
Thanks _for_ *your * feedback. |
|
||||
Thanks _for_ *your * feedback.", 4},
|
|
||||
|
|
||||
new object[] { @"Thanks _for_ *your * feedback.
|
|
||||
Thanks _for_ *your * feedback.", 1}
|
|
||||
}; |
|
||||
} |
|
||||
@ -0,0 +1,11 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Docs.Admin.Documents; |
||||
|
|
||||
|
public class DocumentInfoDto |
||||
|
{ |
||||
|
public string Version { get; set; } |
||||
|
public string Format { get; set; } |
||||
|
public string LanguageCode { get; set; } |
||||
|
public Guid ProjectId { get; set; } |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Docs.Admin.Projects; |
||||
|
|
||||
|
public class ProjectWithoutDetailsDto |
||||
|
{ |
||||
|
public Guid Id { get; set; } |
||||
|
public string Name { get; set; } |
||||
|
} |
||||
@ -1,14 +1,24 @@ |
|||||
|
using System.Collections.Generic; |
||||
using System.Threading.Tasks; |
using System.Threading.Tasks; |
||||
using Microsoft.AspNetCore.Authorization; |
using Microsoft.AspNetCore.Authorization; |
||||
using Microsoft.AspNetCore.Mvc; |
using Microsoft.AspNetCore.Mvc; |
||||
|
using Volo.Docs.Admin.Projects; |
||||
|
|
||||
namespace Volo.Docs.Admin.Pages.Docs.Admin.Documents; |
namespace Volo.Docs.Admin.Pages.Docs.Admin.Documents; |
||||
|
|
||||
[Authorize(DocsAdminPermissions.Projects.Default)] |
[Authorize(DocsAdminPermissions.Projects.Default)] |
||||
public class IndexModel : DocsAdminPageModel |
public class IndexModel : DocsAdminPageModel |
||||
{ |
{ |
||||
public virtual Task<IActionResult> OnGet() |
private readonly IProjectAdminAppService _projectAdminAppService; |
||||
|
public List<ProjectWithoutDetailsDto> Projects { get; set; } |
||||
|
|
||||
|
public IndexModel(IProjectAdminAppService projectAdminAppService) |
||||
|
{ |
||||
|
_projectAdminAppService = projectAdminAppService; |
||||
|
} |
||||
|
public virtual async Task<IActionResult> OnGet() |
||||
{ |
{ |
||||
return Task.FromResult<IActionResult>(Page()); |
Projects = await _projectAdminAppService.GetListWithoutDetailsAsync(); |
||||
|
return Page(); |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,11 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Docs.Documents; |
||||
|
|
||||
|
public class DocumentInfo |
||||
|
{ |
||||
|
public string Version { get; set; } |
||||
|
public string Format { get; set; } |
||||
|
public string LanguageCode { get; set; } |
||||
|
public Guid ProjectId { get; set; } |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue