mirror of https://github.com/Squidex/squidex.git
34 changed files with 680 additions and 42 deletions
@ -0,0 +1,51 @@ |
|||
// ==========================================================================
|
|||
// AssetsField.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Microsoft.OData.Edm; |
|||
using Newtonsoft.Json.Linq; |
|||
using NJsonSchema; |
|||
using Squidex.Core.Schemas.Validators; |
|||
|
|||
namespace Squidex.Core.Schemas |
|||
{ |
|||
public sealed class AssetsField : Field<AssetsFieldProperties> |
|||
{ |
|||
private readonly IAssetTester assetTester; |
|||
|
|||
public AssetsField(long id, string name, AssetsFieldProperties properties, IAssetTester assetTester) |
|||
: base(id, name, properties) |
|||
{ |
|||
this.assetTester = assetTester; |
|||
} |
|||
|
|||
protected override IEnumerable<IValidator> CreateValidators() |
|||
{ |
|||
yield return new AssetsValidator(assetTester, Properties.IsRequired); |
|||
} |
|||
|
|||
public override object ConvertValue(JToken value) |
|||
{ |
|||
return new AssetsValue(value.ToObject<Guid[]>()); |
|||
} |
|||
|
|||
protected override void PrepareJsonSchema(JsonProperty jsonProperty, Func<string, JsonSchema4, JsonSchema4> schemaResolver) |
|||
{ |
|||
var itemSchema = schemaResolver("AssetItem", new JsonSchema4 { Type = JsonObjectType.String }); |
|||
|
|||
jsonProperty.Type = JsonObjectType.Array; |
|||
jsonProperty.Item = itemSchema; |
|||
} |
|||
|
|||
protected override IEdmTypeReference CreateEdmType() |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
// ==========================================================================
|
|||
// AssetsFieldProperties.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Core.Schemas |
|||
{ |
|||
[TypeName("AssetsField")] |
|||
public sealed class AssetsFieldProperties : FieldProperties |
|||
{ |
|||
public override JToken GetDefaultValue() |
|||
{ |
|||
return new JArray(); |
|||
} |
|||
|
|||
protected override IEnumerable<ValidationError> ValidateCore() |
|||
{ |
|||
yield break; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
// ==========================================================================
|
|||
// AssetsValue.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Squidex.Core.Schemas |
|||
{ |
|||
public sealed class AssetsValue |
|||
{ |
|||
private readonly List<Guid> EmptyAssetIds = new List<Guid>(); |
|||
|
|||
public IReadOnlyList<Guid> AssetIds { get; } |
|||
|
|||
public AssetsValue(IReadOnlyList<Guid> assetIds) |
|||
{ |
|||
AssetIds = assetIds ?? EmptyAssetIds; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// ==========================================================================
|
|||
// IAssetTester.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Core.Schemas |
|||
{ |
|||
public interface IAssetTester |
|||
{ |
|||
Task<bool> IsValidAsync(Guid assetId); |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
// ==========================================================================
|
|||
// AssetsValidator.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Core.Schemas.Validators |
|||
{ |
|||
public sealed class AssetsValidator : IValidator |
|||
{ |
|||
private readonly IAssetTester assetTester; |
|||
private readonly bool isRequired; |
|||
|
|||
public AssetsValidator(IAssetTester assetTester, bool isRequired) |
|||
{ |
|||
this.assetTester = assetTester; |
|||
this.isRequired = isRequired; |
|||
} |
|||
|
|||
public async Task ValidateAsync(object value, Action<string> addError) |
|||
{ |
|||
var assets = value as AssetsValue; |
|||
|
|||
if (assets == null || assets.AssetIds.Count == 0) |
|||
{ |
|||
if (isRequired) |
|||
{ |
|||
addError("<FIELD> is required"); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
var assetTasks = assets.AssetIds.Select(CheckAsset).ToArray(); |
|||
|
|||
await Task.WhenAll(assetTasks); |
|||
|
|||
foreach (var notFoundId in assetTasks.Where(x => !x.Result.IsFound).Select(x => x.Result.AssetId)) |
|||
{ |
|||
addError($"<FIELD> contains invalid asset '{notFoundId}'"); |
|||
} |
|||
} |
|||
|
|||
private async Task<(Guid AssetId, bool IsFound)> CheckAsset(Guid id) |
|||
{ |
|||
var isFound = await assetTester.IsValidAsync(id); |
|||
|
|||
return (id, isFound); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
// ==========================================================================
|
|||
// AssetsFieldPropertiesDto.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using NJsonSchema.Annotations; |
|||
using Squidex.Core.Schemas; |
|||
using Squidex.Infrastructure.Reflection; |
|||
|
|||
namespace Squidex.Controllers.Api.Schemas.Models |
|||
{ |
|||
[JsonSchema("Assets")] |
|||
public sealed class AssetsFieldPropertiesDto : FieldPropertiesDto |
|||
{ |
|||
public override FieldProperties ToProperties() |
|||
{ |
|||
return SimpleMapper.Map(this, new AssetsFieldProperties()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
// ==========================================================================
|
|||
// AssetFieldPropertiesTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Core.Schemas |
|||
{ |
|||
public class AssetsFieldPropertiesTests |
|||
{ |
|||
[Fact] |
|||
public void Should_set_or_freeze_sut() |
|||
{ |
|||
var sut = new AssetsFieldProperties(); |
|||
|
|||
foreach (var property in sut.GetType().GetRuntimeProperties().Where(x => x.Name != "IsFrozen")) |
|||
{ |
|||
var value = |
|||
property.PropertyType.GetTypeInfo().IsValueType ? |
|||
Activator.CreateInstance(property.PropertyType) : |
|||
null; |
|||
|
|||
property.SetValue(sut, value); |
|||
|
|||
var result = property.GetValue(sut); |
|||
|
|||
Assert.Equal(value, result); |
|||
} |
|||
|
|||
sut.Freeze(); |
|||
|
|||
foreach (var property in sut.GetType().GetRuntimeProperties().Where(x => x.Name != "IsFrozen")) |
|||
{ |
|||
var value = |
|||
property.PropertyType.GetTypeInfo().IsValueType ? |
|||
Activator.CreateInstance(property.PropertyType) : |
|||
null; |
|||
|
|||
Assert.Throws<InvalidOperationException>(() => |
|||
{ |
|||
try |
|||
{ |
|||
property.SetValue(sut, value); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
throw ex.InnerException; |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,118 @@ |
|||
// ==========================================================================
|
|||
// AssetFieldTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using FluentAssertions; |
|||
using Moq; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Infrastructure.Tasks; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Core.Schemas |
|||
{ |
|||
public class AssetsFieldTests |
|||
{ |
|||
private readonly Mock<IAssetTester> assetTester = new Mock<IAssetTester>(); |
|||
private readonly List<string> errors = new List<string>(); |
|||
|
|||
[Fact] |
|||
public void Should_instantiate_field() |
|||
{ |
|||
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties(), assetTester.Object); |
|||
|
|||
Assert.Equal("my-asset", sut.Name); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_clone_object() |
|||
{ |
|||
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties(), assetTester.Object); |
|||
|
|||
Assert.NotEqual(sut, sut.Enable()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_not_add_error_if_assets_are_valid() |
|||
{ |
|||
var assetId = Guid.NewGuid(); |
|||
|
|||
assetTester.Setup(x => x.IsValidAsync(assetId)).Returns(TaskHelper.True); |
|||
|
|||
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties(), assetTester.Object); |
|||
|
|||
await sut.ValidateAsync(CreateValue(assetId), errors); |
|||
|
|||
Assert.Empty(errors); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_not_add_error_if_assets_are_null_and_valid() |
|||
{ |
|||
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties(), assetTester.Object); |
|||
|
|||
await sut.ValidateAsync(CreateValue(null), errors); |
|||
|
|||
Assert.Empty(errors); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_errors_if_assets_are_required_and_null() |
|||
{ |
|||
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties { IsRequired = true }, assetTester.Object); |
|||
|
|||
await sut.ValidateAsync(CreateValue(null), errors); |
|||
|
|||
errors.ShouldBeEquivalentTo( |
|||
new[] { "<FIELD> is required" }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_errors_if_assets_are_required_and_empty() |
|||
{ |
|||
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties { IsRequired = true }, assetTester.Object); |
|||
|
|||
await sut.ValidateAsync(CreateValue(), errors); |
|||
|
|||
errors.ShouldBeEquivalentTo( |
|||
new[] { "<FIELD> is required" }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_errors_if_value_is_not_valid() |
|||
{ |
|||
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties(), assetTester.Object); |
|||
|
|||
await sut.ValidateAsync("invalid", errors); |
|||
|
|||
errors.ShouldBeEquivalentTo( |
|||
new[] { "<FIELD> is not a valid value" }); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_errors_if_asset_are_not_valid() |
|||
{ |
|||
var assetId = Guid.NewGuid(); |
|||
|
|||
assetTester.Setup(x => x.IsValidAsync(assetId)).Returns(TaskHelper.False); |
|||
|
|||
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties(), assetTester.Object); |
|||
|
|||
await sut.ValidateAsync(CreateValue(assetId), errors); |
|||
|
|||
errors.ShouldBeEquivalentTo( |
|||
new[] { $"<FIELD> contains invalid asset '{assetId}'" }); |
|||
} |
|||
|
|||
private static JToken CreateValue(params Guid[] ids) |
|||
{ |
|||
return ids == null ? JValue.CreateNull() : (JToken)new JArray(ids); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
// ==========================================================================
|
|||
// ImageSharpAssetThumbnailGeneratorTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Infrastructure.Assets.ImageSharp; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Infrastructure.Assets |
|||
{ |
|||
public class ImageSharpAssetThumbnailGeneratorTests |
|||
{ |
|||
private const string Image = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTM0A1t6AAAADElEQVQYV2P4//8/AAX+Av6nNYGEAAAAAElFTkSuQmCC"; |
|||
private readonly ImageSharpAssetThumbnailGenerator sut = new ImageSharpAssetThumbnailGenerator(); |
|||
|
|||
[Fact] |
|||
public async Task Should_return_same_image_if_no_size_is_passed_for_thumbnail() |
|||
{ |
|||
var source = new MemoryStream(Convert.FromBase64String(Image)); |
|||
var target = new MemoryStream(); |
|||
|
|||
await sut.CreateThumbnailAsync(source, target, null, null, "resize"); |
|||
|
|||
Assert.Equal(target.Length, source.Length); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_resize_image_to_target() |
|||
{ |
|||
var source = new MemoryStream(Convert.FromBase64String(Image)); |
|||
var target = new MemoryStream(); |
|||
|
|||
await sut.CreateThumbnailAsync(source, target, 100, 100, "resize"); |
|||
|
|||
Assert.True(target.Length > source.Length); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_return_image_information_if_image_is_valid() |
|||
{ |
|||
var source = new MemoryStream(Convert.FromBase64String(Image)); |
|||
|
|||
var imageInfo = await sut.GetImageInfoAsync(source); |
|||
|
|||
Assert.Equal(1, imageInfo.PixelHeight); |
|||
Assert.Equal(1, imageInfo.PixelWidth); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_return_null_if_stream_is_not_an_image() |
|||
{ |
|||
var source = new MemoryStream(Convert.FromBase64String("YXNkc2Fk")); |
|||
|
|||
var imageInfo = await sut.GetImageInfoAsync(source); |
|||
|
|||
Assert.Null(imageInfo); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,161 @@ |
|||
// ==========================================================================
|
|||
// SemanticLogAdapterTests.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Microsoft.Extensions.Logging; |
|||
using Moq; |
|||
using Squidex.Infrastructure.Log.Adapter; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Infrastructure.Log |
|||
{ |
|||
public class SemanticLogAdapterTests |
|||
{ |
|||
private readonly List<ILogChannel> channels = new List<ILogChannel>(); |
|||
private readonly Lazy<SemanticLog> log; |
|||
private readonly Mock<ILogChannel> channel = new Mock<ILogChannel>(); |
|||
private readonly SemanticLogLoggerProvider sut; |
|||
private string output; |
|||
|
|||
public SemanticLog Log |
|||
{ |
|||
get { return log.Value; } |
|||
} |
|||
|
|||
public SemanticLogAdapterTests() |
|||
{ |
|||
channels.Add(channel.Object); |
|||
|
|||
channel.Setup(x => x.Log(It.IsAny<SemanticLogLevel>(), It.IsAny<string>())).Callback( |
|||
new Action<SemanticLogLevel, string>((level, message) => |
|||
{ |
|||
output = message; |
|||
})); |
|||
|
|||
log = new Lazy<SemanticLog>(() => new SemanticLog(channels, new List<ILogAppender>(), () => new JsonLogWriter())); |
|||
|
|||
sut = new SemanticLogLoggerProvider(log.Value); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_do_nothing_when_disposing() |
|||
{ |
|||
sut.Dispose(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_provide_a_scope() |
|||
{ |
|||
var logger = sut.CreateLogger("test-category"); |
|||
|
|||
Assert.NotNull(logger.BeginScope(1)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_always() |
|||
{ |
|||
var logger = sut.CreateLogger("test-category"); |
|||
|
|||
Assert.True(logger.IsEnabled(LogLevel.Critical)); |
|||
Assert.True(logger.IsEnabled((LogLevel)123)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_message_with_event_id() |
|||
{ |
|||
var eventId = new EventId(1000); |
|||
|
|||
var logger = sut.CreateLogger("my-category"); |
|||
|
|||
logger.Log(LogLevel.Debug, eventId, 1, null, (x, e) => "my-message"); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Debug") |
|||
.WriteProperty("message", "my-message") |
|||
.WriteObject("eventId", e => e |
|||
.WriteProperty("id", 1000)) |
|||
.WriteProperty("category", "my-category")); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_message_with_event_id_and_name() |
|||
{ |
|||
var eventId = new EventId(1000, "my-event"); |
|||
|
|||
var logger = sut.CreateLogger("my-category"); |
|||
|
|||
logger.Log(LogLevel.Debug, eventId, 1, null, (x, e) => "my-message"); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Debug") |
|||
.WriteProperty("message", "my-message") |
|||
.WriteObject("eventId", e => e |
|||
.WriteProperty("id", 1000) |
|||
.WriteProperty("name", "my-event")) |
|||
.WriteProperty("category", "my-category")); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_log_message_with_exception() |
|||
{ |
|||
var exception = new InvalidOperationException(); |
|||
|
|||
var logger = sut.CreateLogger("my-category"); |
|||
|
|||
logger.Log(LogLevel.Debug, new EventId(0), 1, exception, (x, e) => "my-message"); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", "Debug") |
|||
.WriteProperty("message", "my-message") |
|||
.WriteException(exception) |
|||
.WriteProperty("category", "my-category")); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(LogLevel.None, "Debug")] |
|||
[InlineData(LogLevel.Debug, "Debug")] |
|||
[InlineData(LogLevel.Error, "Error")] |
|||
[InlineData(LogLevel.Trace, "Trace")] |
|||
[InlineData(LogLevel.Warning, "Warning")] |
|||
[InlineData(LogLevel.Critical, "Fatal")] |
|||
[InlineData(LogLevel.Information, "Information")] |
|||
public void Should_log_message(LogLevel level, string semanticLogLevel) |
|||
{ |
|||
var logger = sut.CreateLogger("my-category"); |
|||
|
|||
logger.Log(level, new EventId(0), 1, null, (x, e) => "my-message"); |
|||
|
|||
var expected = |
|||
MakeTestCall(w => w |
|||
.WriteProperty("logLevel", semanticLogLevel) |
|||
.WriteProperty("message", "my-message") |
|||
.WriteProperty("category", "my-category")); |
|||
|
|||
Assert.Equal(expected, output); |
|||
} |
|||
|
|||
private static string MakeTestCall(Action<IObjectWriter> writer) |
|||
{ |
|||
IObjectWriter sut = new JsonLogWriter(); |
|||
|
|||
writer(sut); |
|||
|
|||
return sut.ToString(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue