mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
81 changed files with 1564 additions and 604 deletions
@ -0,0 +1,15 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.ValidateContent |
|||
{ |
|||
public enum ValidationMode |
|||
{ |
|||
Default, |
|||
Optimized |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Entities.Assets.State; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.Orleans; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Assets |
|||
{ |
|||
public sealed class AssetDomainObjectGrain : DomainObjectGrain<AssetDomainObject, AssetState>, IAssetGrain |
|||
{ |
|||
private static readonly TimeSpan Lifetime = TimeSpan.FromMinutes(5); |
|||
|
|||
public AssetDomainObjectGrain(IServiceProvider serviceProvider, IActivationLimit limit) |
|||
: base(serviceProvider) |
|||
{ |
|||
limit?.SetLimit(5000, Lifetime); |
|||
} |
|||
|
|||
protected override Task OnActivateAsync(Guid key) |
|||
{ |
|||
TryDelayDeactivation(Lifetime); |
|||
|
|||
return base.OnActivateAsync(key); |
|||
} |
|||
|
|||
public async Task<J<IAssetEntity>> GetStateAsync(long version = EtagVersion.Any) |
|||
{ |
|||
await DomainObject.EnsureLoadedAsync(); |
|||
|
|||
return DomainObject.GetSnapshot(version); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Entities.Assets.State; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.Orleans; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Assets |
|||
{ |
|||
public sealed class AssetFolderDomainObjectGrain : DomainObjectGrain<AssetFolderDomainObject, AssetFolderState>, IAssetFolderGrain |
|||
{ |
|||
private static readonly TimeSpan Lifetime = TimeSpan.FromMinutes(5); |
|||
|
|||
public AssetFolderDomainObjectGrain(IServiceProvider serviceProvider, IActivationLimit limit) |
|||
: base(serviceProvider) |
|||
{ |
|||
limit?.SetLimit(5000, Lifetime); |
|||
} |
|||
|
|||
protected override Task OnActivateAsync(Guid key) |
|||
{ |
|||
TryDelayDeactivation(Lifetime); |
|||
|
|||
return base.OnActivateAsync(key); |
|||
} |
|||
|
|||
public async Task<J<IAssetFolderEntity>> GetStateAsync() |
|||
{ |
|||
await DomainObject.EnsureLoadedAsync(); |
|||
|
|||
return Snapshot; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Commands |
|||
{ |
|||
public sealed class CreateContents : SquidexCommand, ISchemaCommand, IAppCommand |
|||
{ |
|||
public NamedId<Guid> AppId { get; set; } |
|||
|
|||
public NamedId<Guid> SchemaId { get; set; } |
|||
|
|||
public bool Publish { get; set; } |
|||
|
|||
public bool DoNotValidate { get; set; } |
|||
|
|||
public bool DoNotScript { get; set; } |
|||
|
|||
public bool OptimizeValidation { get; set; } |
|||
|
|||
public List<NamedContentData> Datas { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Entities.Contents.State; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.Orleans; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents |
|||
{ |
|||
public sealed class ContentDomainObjectGrain : DomainObjectGrain<ContentDomainObject, ContentState>, IContentGrain |
|||
{ |
|||
private static readonly TimeSpan Lifetime = TimeSpan.FromMinutes(5); |
|||
|
|||
public ContentDomainObjectGrain(IServiceProvider serviceProvider, IActivationLimit limit) |
|||
: base(serviceProvider) |
|||
{ |
|||
limit?.SetLimit(5000, Lifetime); |
|||
} |
|||
|
|||
public async Task<J<IContentEntity>> GetStateAsync(long version = -2) |
|||
{ |
|||
await DomainObject.EnsureLoadedAsync(); |
|||
|
|||
return DomainObject.GetSnapshot(version); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Squidex.Domain.Apps.Entities.Contents.Commands; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.Reflection; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents |
|||
{ |
|||
public sealed class ContentImporterCommandMiddleware : ICommandMiddleware |
|||
{ |
|||
private readonly IServiceProvider serviceProvider; |
|||
|
|||
public ContentImporterCommandMiddleware(IServiceProvider serviceProvider) |
|||
{ |
|||
Guard.NotNull(serviceProvider); |
|||
|
|||
this.serviceProvider = serviceProvider; |
|||
} |
|||
|
|||
public async Task HandleAsync(CommandContext context, NextDelegate next) |
|||
{ |
|||
if (context.Command is CreateContents createContents) |
|||
{ |
|||
var result = new ImportResult(); |
|||
|
|||
if (createContents.Datas != null && createContents.Datas.Count > 0) |
|||
{ |
|||
var command = SimpleMapper.Map(createContents, new CreateContent()); |
|||
|
|||
foreach (var data in createContents.Datas) |
|||
{ |
|||
try |
|||
{ |
|||
command.ContentId = Guid.NewGuid(); |
|||
command.Data = data; |
|||
|
|||
var content = serviceProvider.GetRequiredService<ContentDomainObject>(); |
|||
|
|||
content.Setup(command.ContentId); |
|||
|
|||
await content.ExecuteAsync(command); |
|||
|
|||
result.Add(new ImportResultItem { ContentId = command.ContentId }); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
result.Add(new ImportResultItem { Exception = ex }); |
|||
} |
|||
} |
|||
} |
|||
|
|||
context.Complete(result); |
|||
} |
|||
else |
|||
{ |
|||
await next(context); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents |
|||
{ |
|||
public sealed class ImportResult : List<ImportResultItem> |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents |
|||
{ |
|||
public sealed class ImportResultItem |
|||
{ |
|||
public Guid? ContentId { get; set; } |
|||
|
|||
public Exception? Exception { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Entities.Rules.State; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.Orleans; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Rules |
|||
{ |
|||
public sealed class RuleDomainObjectGrain : DomainObjectGrain<RuleDomainObject, RuleState>, IRuleGrain |
|||
{ |
|||
public RuleDomainObjectGrain(IServiceProvider serviceProvider) |
|||
: base(serviceProvider) |
|||
{ |
|||
} |
|||
|
|||
public async Task<J<IRuleEntity>> GetStateAsync() |
|||
{ |
|||
await DomainObject.EnsureLoadedAsync(); |
|||
|
|||
return Snapshot; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Entities.Schemas.State; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.Orleans; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Schemas |
|||
{ |
|||
public sealed class SchemaDomainObjectGrain : DomainObjectGrain<SchemaDomainObject, SchemaState>, ISchemaGrain |
|||
{ |
|||
public SchemaDomainObjectGrain(IServiceProvider serviceProvider) |
|||
: base(serviceProvider) |
|||
{ |
|||
} |
|||
|
|||
public async Task<J<ISchemaEntity>> GetStateAsync() |
|||
{ |
|||
await DomainObject.EnsureLoadedAsync(); |
|||
|
|||
return Snapshot; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.Log; |
|||
using Squidex.Infrastructure.States; |
|||
|
|||
namespace Squidex.Infrastructure.Commands |
|||
{ |
|||
public abstract class DomainObject<T> : DomainObjectBase<T> where T : class, IDomainState<T>, new() |
|||
{ |
|||
private readonly IStore<Guid> store; |
|||
private T snapshot = new T { Version = EtagVersion.Empty }; |
|||
private IPersistence<T>? persistence; |
|||
|
|||
public override T Snapshot |
|||
{ |
|||
get { return snapshot; } |
|||
} |
|||
|
|||
protected DomainObject(IStore<Guid> store, ISemanticLog log) |
|||
: base(log) |
|||
{ |
|||
Guard.NotNull(store); |
|||
|
|||
this.store = store; |
|||
} |
|||
|
|||
protected override void OnSetup() |
|||
{ |
|||
persistence = store.WithSnapshotsAndEventSourcing(GetType(), Id, new HandleSnapshot<T>(ApplySnapshot), x => ApplyEvent(x, true)); |
|||
} |
|||
|
|||
protected sealed override bool ApplyEvent(Envelope<IEvent> @event, bool isLoading) |
|||
{ |
|||
var newVersion = Version + 1; |
|||
|
|||
var newSnapshot = OnEvent(@event); |
|||
|
|||
if (!ReferenceEquals(Snapshot, newSnapshot) || isLoading) |
|||
{ |
|||
snapshot = newSnapshot; |
|||
snapshot.Version = newVersion; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
protected sealed override void RestorePreviousSnapshot(T previousSnapshot, long previousVersion) |
|||
{ |
|||
snapshot = previousSnapshot; |
|||
} |
|||
|
|||
private void ApplySnapshot(T state) |
|||
{ |
|||
snapshot = state; |
|||
} |
|||
|
|||
protected sealed override async Task WriteAsync(Envelope<IEvent>[] newEvents, long previousVersion) |
|||
{ |
|||
if (newEvents.Length > 0 && persistence != null) |
|||
{ |
|||
await persistence.WriteEventsAsync(newEvents); |
|||
await persistence.WriteSnapshotAsync(Snapshot); |
|||
} |
|||
} |
|||
|
|||
protected async sealed override Task ReadAsync() |
|||
{ |
|||
if (persistence != null) |
|||
{ |
|||
await persistence.ReadAsync(); |
|||
} |
|||
} |
|||
|
|||
public async sealed override Task RebuildStateAsync() |
|||
{ |
|||
if (persistence != null) |
|||
{ |
|||
await persistence.WriteSnapshotAsync(Snapshot); |
|||
} |
|||
} |
|||
|
|||
protected T OnEvent(Envelope<IEvent> @event) |
|||
{ |
|||
return Snapshot.Apply(@event); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,129 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.Linq; |
|||
using System.Security; |
|||
using System.Text; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Validation; |
|||
|
|||
namespace Squidex.Web |
|||
{ |
|||
public static class ApiExceptionConverter |
|||
{ |
|||
private static readonly List<Func<Exception, ErrorDto?>> Handlers = new List<Func<Exception, ErrorDto?>>(); |
|||
private static readonly Dictionary<int, string> Links = new Dictionary<int, string> |
|||
{ |
|||
[400] = "https://tools.ietf.org/html/rfc7231#section-6.5.1", |
|||
[401] = "https://tools.ietf.org/html/rfc7235#section-3.1", |
|||
[403] = "https://tools.ietf.org/html/rfc7231#section-6.5.3", |
|||
[404] = "https://tools.ietf.org/html/rfc7231#section-6.5.4", |
|||
[406] = "https://tools.ietf.org/html/rfc7231#section-6.5.6", |
|||
[409] = "https://tools.ietf.org/html/rfc7231#section-6.5.8", |
|||
[412] = "https://tools.ietf.org/html/rfc7231#section-6.5.10", |
|||
[415] = "https://tools.ietf.org/html/rfc7231#section-6.5.13", |
|||
[422] = "https://tools.ietf.org/html/rfc4918#section-11.2", |
|||
[500] = "https://tools.ietf.org/html/rfc7231#section-6.6.1", |
|||
}; |
|||
|
|||
private static void AddHandler<T>(Func<T, ErrorDto> handler) where T : Exception |
|||
{ |
|||
Handlers.Add(ex => ex is T typed ? handler(typed) : null); |
|||
} |
|||
|
|||
static ApiExceptionConverter() |
|||
{ |
|||
AddHandler<ValidationException>(OnValidationException); |
|||
AddHandler<DecoderFallbackException>(OnDecoderException); |
|||
AddHandler<DomainObjectNotFoundException>(OnDomainObjectNotFoundException); |
|||
AddHandler<DomainObjectVersionException>(OnDomainObjectVersionException); |
|||
AddHandler<DomainForbiddenException>(OnDomainForbiddenException); |
|||
AddHandler<DomainException>(OnDomainException); |
|||
AddHandler<SecurityException>(OnSecurityException); |
|||
} |
|||
|
|||
public static ErrorDto ToErrorDto(this Exception exception, HttpContext? httpContext) |
|||
{ |
|||
Guard.NotNull(exception); |
|||
|
|||
ErrorDto? result = null; |
|||
|
|||
foreach (var handler in Handlers) |
|||
{ |
|||
result = handler(exception); |
|||
|
|||
if (result != null) |
|||
{ |
|||
result.TraceId = Activity.Current?.Id ?? httpContext?.TraceIdentifier; |
|||
|
|||
if (result.StatusCode.HasValue) |
|||
{ |
|||
result.Type = Links.GetOrDefault(result.StatusCode.Value); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
|
|||
return new ErrorDto { StatusCode = 500 }; |
|||
} |
|||
|
|||
private static ErrorDto OnDecoderException(DecoderFallbackException ex) |
|||
{ |
|||
return new ErrorDto { StatusCode = 400, Message = ex.Message }; |
|||
} |
|||
|
|||
private static ErrorDto OnDomainObjectNotFoundException(DomainObjectNotFoundException ex) |
|||
{ |
|||
return new ErrorDto { StatusCode = 404 }; |
|||
} |
|||
|
|||
private static ErrorDto OnDomainObjectVersionException(DomainObjectVersionException ex) |
|||
{ |
|||
return new ErrorDto { StatusCode = 412, Message = ex.Message }; |
|||
} |
|||
|
|||
private static ErrorDto OnDomainException(DomainException ex) |
|||
{ |
|||
return new ErrorDto { StatusCode = 400, Message = ex.Message }; |
|||
} |
|||
|
|||
private static ErrorDto OnDomainForbiddenException(DomainForbiddenException ex) |
|||
{ |
|||
return new ErrorDto { StatusCode = 403, Message = ex.Message }; |
|||
} |
|||
|
|||
private static ErrorDto OnSecurityException(SecurityException ex) |
|||
{ |
|||
return new ErrorDto { StatusCode = 403, Message = ex.Message }; |
|||
} |
|||
|
|||
private static ErrorDto OnValidationException(ValidationException ex) |
|||
{ |
|||
return new ErrorDto { StatusCode = 400, Message = ex.Summary, Details = ToDetails(ex) }; |
|||
} |
|||
|
|||
private static string[] ToDetails(ValidationException ex) |
|||
{ |
|||
return ex.Errors?.Select(e => |
|||
{ |
|||
if (e.PropertyNames?.Any() == true) |
|||
{ |
|||
return $"{string.Join(", ", e.PropertyNames)}: {e.Message}"; |
|||
} |
|||
else |
|||
{ |
|||
return e.Message; |
|||
} |
|||
}).ToArray() ?? new string[0]; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Entities.Contents.Commands; |
|||
using Squidex.Infrastructure.Reflection; |
|||
|
|||
namespace Squidex.Areas.Api.Controllers.Contents.Models |
|||
{ |
|||
public sealed class ImportContentsDto |
|||
{ |
|||
/// <summary>
|
|||
/// The data to import.
|
|||
/// </summary>
|
|||
[Required] |
|||
public List<NamedContentData> Datas { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// True to automatically publish the content.
|
|||
/// </summary>
|
|||
public bool Publish { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// True to turn off scripting for faster inserts. Default: true.
|
|||
/// </summary>
|
|||
public bool DoNotScript { get; set; } = true; |
|||
|
|||
/// <summary>
|
|||
/// True to turn off costly validation: Unique checks, asset checks and reference checks. Default: true.
|
|||
/// </summary>
|
|||
public bool OptimizeValidation { get; set; } = true; |
|||
|
|||
public CreateContents ToCommand() |
|||
{ |
|||
return SimpleMapper.Map(this, new CreateContents()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Squidex.Domain.Apps.Entities.Contents; |
|||
using Squidex.Web; |
|||
|
|||
namespace Squidex.Areas.Api.Controllers.Contents.Models |
|||
{ |
|||
public sealed class ImportResultDto |
|||
{ |
|||
/// <summary>
|
|||
/// The error when the import failed.
|
|||
/// </summary>
|
|||
public ErrorDto? Error { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The id of the content when the import succeeds.
|
|||
/// </summary>
|
|||
public Guid? ContentId { get; set; } |
|||
|
|||
public static ImportResultDto FromImportResult(ImportResultItem result, HttpContext httpContext) |
|||
{ |
|||
return new ImportResultDto { ContentId = result.ContentId, Error = result.Exception?.ToErrorDto(httpContext) }; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using FakeItEasy; |
|||
using Squidex.Infrastructure.Orleans; |
|||
using Xunit; |
|||
|
|||
#pragma warning disable RECS0026 // Possible unassigned object created by 'new'
|
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Assets |
|||
{ |
|||
public class AssetDomainObjectGrainTests |
|||
{ |
|||
private readonly IActivationLimit limit = A.Fake<IActivationLimit>(); |
|||
|
|||
[Fact] |
|||
public void Should_set_limit() |
|||
{ |
|||
var serviceProvider = A.Fake<IServiceProvider>(); |
|||
|
|||
A.CallTo(() => serviceProvider.GetService(typeof(AssetDomainObject))) |
|||
.Returns(A.Dummy<AssetDomainObject>()); |
|||
|
|||
new AssetDomainObjectGrain(serviceProvider, limit); |
|||
|
|||
A.CallTo(() => limit.SetLimit(5000, TimeSpan.FromMinutes(5))) |
|||
.MustHaveHappened(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using FakeItEasy; |
|||
using Squidex.Infrastructure.Orleans; |
|||
using Xunit; |
|||
|
|||
#pragma warning disable RECS0026 // Possible unassigned object created by 'new'
|
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Assets |
|||
{ |
|||
public class AssetFolderDomainObjectGrainTests |
|||
{ |
|||
private readonly IActivationLimit limit = A.Fake<IActivationLimit>(); |
|||
|
|||
[Fact] |
|||
public void Should_set_limit() |
|||
{ |
|||
var serviceProvider = A.Fake<IServiceProvider>(); |
|||
|
|||
A.CallTo(() => serviceProvider.GetService(typeof(AssetFolderDomainObject))) |
|||
.Returns(A.Dummy<AssetFolderDomainObject>()); |
|||
|
|||
new AssetFolderDomainObjectGrain(serviceProvider, limit); |
|||
|
|||
A.CallTo(() => limit.SetLimit(5000, TimeSpan.FromMinutes(5))) |
|||
.MustHaveHappened(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using FakeItEasy; |
|||
using Squidex.Infrastructure.Orleans; |
|||
using Xunit; |
|||
|
|||
#pragma warning disable RECS0026 // Possible unassigned object created by 'new'
|
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents |
|||
{ |
|||
public class ContentDomainObjectGrainTests |
|||
{ |
|||
private readonly IActivationLimit limit = A.Fake<IActivationLimit>(); |
|||
|
|||
[Fact] |
|||
public void Should_set_limit() |
|||
{ |
|||
var serviceProvider = A.Fake<IServiceProvider>(); |
|||
|
|||
A.CallTo(() => serviceProvider.GetService(typeof(ContentDomainObject))) |
|||
.Returns(A.Dummy<ContentDomainObject>()); |
|||
|
|||
new ContentDomainObjectGrain(serviceProvider, limit); |
|||
|
|||
A.CallTo(() => limit.SetLimit(5000, TimeSpan.FromMinutes(5))) |
|||
.MustHaveHappened(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,142 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Entities.Contents.Commands; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.Json.Objects; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents |
|||
{ |
|||
public class ContentImporterCommandMiddlewareTests |
|||
{ |
|||
private readonly IServiceProvider serviceProvider = A.Fake<IServiceProvider>(); |
|||
private readonly ICommandBus commandBus = A.Dummy<ICommandBus>(); |
|||
private readonly ContentImporterCommandMiddleware sut; |
|||
|
|||
public ContentImporterCommandMiddlewareTests() |
|||
{ |
|||
sut = new ContentImporterCommandMiddleware(serviceProvider); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_do_nothing_if_datas_is_null() |
|||
{ |
|||
var command = new CreateContents(); |
|||
|
|||
var context = new CommandContext(command, commandBus); |
|||
|
|||
await sut.HandleAsync(context); |
|||
|
|||
Assert.True(context.PlainResult is ImportResult); |
|||
|
|||
A.CallTo(() => serviceProvider.GetService(A<Type>.Ignored)) |
|||
.MustNotHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_do_nothing_if_datas_is_empty() |
|||
{ |
|||
var command = new CreateContents { Datas = new List<NamedContentData>() }; |
|||
|
|||
var context = new CommandContext(command, commandBus); |
|||
|
|||
await sut.HandleAsync(context); |
|||
|
|||
Assert.True(context.PlainResult is ImportResult); |
|||
|
|||
A.CallTo(() => serviceProvider.GetService(A<Type>.Ignored)) |
|||
.MustNotHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_import_data() |
|||
{ |
|||
var data1 = CreateData(1); |
|||
var data2 = CreateData(2); |
|||
|
|||
var domainObject = A.Fake<ContentDomainObject>(); |
|||
|
|||
A.CallTo(() => serviceProvider.GetService(typeof(ContentDomainObject))) |
|||
.Returns(domainObject); |
|||
|
|||
var command = new CreateContents |
|||
{ |
|||
Datas = new List<NamedContentData> |
|||
{ |
|||
data1, |
|||
data2 |
|||
} |
|||
}; |
|||
|
|||
var context = new CommandContext(command, commandBus); |
|||
|
|||
await sut.HandleAsync(context); |
|||
|
|||
var result = context.Result<ImportResult>(); |
|||
|
|||
Assert.Equal(2, result.Count); |
|||
Assert.Equal(2, result.Count(x => x.ContentId.HasValue && x.Exception == null)); |
|||
|
|||
A.CallTo(() => domainObject.Setup(A<Guid>.Ignored)) |
|||
.MustHaveHappenedTwiceExactly(); |
|||
|
|||
A.CallTo(() => domainObject.ExecuteAsync(A<CreateContent>.Ignored)) |
|||
.MustHaveHappenedTwiceExactly(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_skip_exception() |
|||
{ |
|||
var data1 = CreateData(1); |
|||
var data2 = CreateData(2); |
|||
|
|||
var domainObject = A.Fake<ContentDomainObject>(); |
|||
|
|||
var exception = new InvalidOperationException(); |
|||
|
|||
A.CallTo(() => serviceProvider.GetService(typeof(ContentDomainObject))) |
|||
.Returns(domainObject); |
|||
|
|||
A.CallTo(() => domainObject.ExecuteAsync(A<CreateContent>.That.Matches(x => x.Data == data1))) |
|||
.Throws(exception); |
|||
|
|||
var command = new CreateContents |
|||
{ |
|||
Datas = new List<NamedContentData> |
|||
{ |
|||
data1, |
|||
data2 |
|||
} |
|||
}; |
|||
|
|||
var context = new CommandContext(command, commandBus); |
|||
|
|||
await sut.HandleAsync(context); |
|||
|
|||
var result = context.Result<ImportResult>(); |
|||
|
|||
Assert.Equal(2, result.Count); |
|||
Assert.Equal(1, result.Count(x => x.ContentId.HasValue && x.Exception == null)); |
|||
Assert.Equal(1, result.Count(x => !x.ContentId.HasValue && x.Exception == exception)); |
|||
} |
|||
|
|||
private static NamedContentData CreateData(int value) |
|||
{ |
|||
return new NamedContentData() |
|||
.AddField("value", |
|||
new ContentFieldData() |
|||
.AddJsonValue("iv", JsonValue.Create(value))); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue