Browse Source

UI updated.

pull/200/head
Sebastian Stehle 8 years ago
parent
commit
9c2c5e036f
  1. 151
      src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs
  2. 27
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/IAssetInfo.cs
  3. 8
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidationContext.cs
  4. 96
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/AssetsValidator.cs
  5. 2
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidatorsFactory.cs
  6. 6
      src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetEntity.cs
  7. 9
      src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetRepository.cs
  8. 1
      src/Squidex.Domain.Apps.Read.MongoDb/History/MongoHistoryEventEntity.cs
  9. 20
      src/Squidex.Domain.Apps.Read/Assets/IAssetEntity.cs
  10. 2
      src/Squidex.Domain.Apps.Read/Assets/Repositories/IAssetRepository.cs
  11. 7
      src/Squidex.Domain.Apps.Read/Contents/IContentEntity.cs
  12. 16
      src/Squidex.Domain.Apps.Read/Contents/QueryContext.cs
  13. 4
      src/Squidex.Domain.Apps.Read/History/IHistoryEventEntity.cs
  14. 2
      src/Squidex.Domain.Apps.Read/IEntityWithAppRef.cs
  15. 7
      src/Squidex.Domain.Apps.Read/Rules/IRuleEntity.cs
  16. 7
      src/Squidex.Domain.Apps.Read/Schemas/ISchemaEntity.cs
  17. 14
      src/Squidex.Domain.Apps.Write/Contents/ContentOperationContext.cs
  18. 28
      src/Squidex.Domain.Apps.Write/Schemas/Guards/FieldPropertiesValidator.cs
  19. 5
      src/Squidex.Infrastructure/FileExtensions.cs
  20. 12
      src/Squidex.Infrastructure/States/StateFactory.cs
  21. 24
      src/Squidex/Areas/Api/Controllers/Schemas/Models/Converters/FieldPropertiesDtoFactory.cs
  22. 60
      src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/AssetsFieldPropertiesDto.cs
  23. 4
      src/Squidex/app/features/administration/pages/users/user-page.component.html
  24. 4
      src/Squidex/app/features/schemas/pages/schema/field.component.html
  25. 90
      src/Squidex/app/features/schemas/pages/schema/types/assets-validation.component.html
  26. 6
      src/Squidex/app/features/schemas/pages/schema/types/assets-validation.component.scss
  27. 34
      src/Squidex/app/features/schemas/pages/schema/types/assets-validation.component.ts
  28. 4
      src/Squidex/app/features/schemas/pages/schema/types/number-validation.component.html
  29. 2
      src/Squidex/app/features/schemas/pages/schema/types/number-validation.component.scss
  30. 4
      src/Squidex/app/features/schemas/pages/schema/types/references-validation.component.html
  31. 2
      src/Squidex/app/features/schemas/pages/schema/types/references-validation.component.scss
  32. 4
      src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.html
  33. 2
      src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.scss
  34. 4
      src/Squidex/app/features/schemas/pages/schema/types/tags-validation.component.html
  35. 2
      src/Squidex/app/features/schemas/pages/schema/types/tags-validation.component.scss
  36. 4
      src/Squidex/app/features/schemas/pages/schemas/schema-form.component.html
  37. 2
      src/Squidex/app/framework/angular/control-errors.component.ts
  38. 2
      src/Squidex/app/framework/angular/dialog-renderer.component.html
  39. 8
      src/Squidex/app/framework/angular/dialog-renderer.component.scss
  40. 26
      src/Squidex/app/framework/angular/http-extensions-impl.ts
  41. 12
      src/Squidex/app/shared/services/schemas.service.ts
  42. 190
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs
  43. 17
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/BooleanFieldTests.cs
  44. 19
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/DateTimeFieldTests.cs
  45. 19
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/GeolocationFieldTests.cs
  46. 11
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/JsonFieldTests.cs
  47. 19
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/NumberFieldTests.cs
  48. 25
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs
  49. 21
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/StringFieldTests.cs
  50. 21
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs
  51. 19
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs
  52. 2
      tests/Squidex.Domain.Apps.Read.Tests/Contents/TestData/FakeAssetEntity.cs
  53. 72
      tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/FieldProperties/AssetsFieldPropertiesTests.cs

151
src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs

@ -6,6 +6,7 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System.Collections.Immutable;
using Squidex.Infrastructure; using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Schemas namespace Squidex.Domain.Apps.Core.Schemas
@ -13,8 +14,32 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(AssetsField))] [TypeName(nameof(AssetsField))]
public sealed class AssetsFieldProperties : FieldProperties public sealed class AssetsFieldProperties : FieldProperties
{ {
private bool mustBeImage;
private int? minItems; private int? minItems;
private int? maxItems; private int? maxItems;
private int? minWidth;
private int? maxWidth;
private int? minHeight;
private int? maxHeight;
private int? minSize;
private int? maxSize;
private int? aspectWidth;
private int? aspectHeight;
private ImmutableList<string> allowedExtensions;
public bool MustBeImage
{
get
{
return mustBeImage;
}
set
{
ThrowIfFrozen();
mustBeImage = value;
}
}
public int? MinItems public int? MinItems
{ {
@ -44,6 +69,132 @@ namespace Squidex.Domain.Apps.Core.Schemas
} }
} }
public int? MinWidth
{
get
{
return minWidth;
}
set
{
ThrowIfFrozen();
minWidth = value;
}
}
public int? MaxWidth
{
get
{
return maxWidth;
}
set
{
ThrowIfFrozen();
maxWidth = value;
}
}
public int? MinHeight
{
get
{
return minHeight;
}
set
{
ThrowIfFrozen();
minHeight = value;
}
}
public int? MaxHeight
{
get
{
return maxHeight;
}
set
{
ThrowIfFrozen();
maxHeight = value;
}
}
public int? MinSize
{
get
{
return minSize;
}
set
{
ThrowIfFrozen();
minSize = value;
}
}
public int? MaxSize
{
get
{
return maxSize;
}
set
{
ThrowIfFrozen();
maxSize = value;
}
}
public int? AspectWidth
{
get
{
return aspectWidth;
}
set
{
ThrowIfFrozen();
aspectWidth = value;
}
}
public int? AspectHeight
{
get
{
return aspectHeight;
}
set
{
ThrowIfFrozen();
aspectHeight = value;
}
}
public ImmutableList<string> AllowedExtensions
{
get
{
return allowedExtensions;
}
set
{
ThrowIfFrozen();
allowedExtensions = value;
}
}
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor) public override T Accept<T>(IFieldPropertiesVisitor<T> visitor)
{ {
return visitor.Visit(this); return visitor.Visit(this);

27
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/IAssetInfo.cs

@ -0,0 +1,27 @@
// ==========================================================================
// IAssetInfo.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
namespace Squidex.Domain.Apps.Core.ValidateContent
{
public interface IAssetInfo
{
Guid AssetId { get; }
long FileSize { get; }
bool IsImage { get; }
int? PixelWidth { get; }
int? PixelHeight { get; }
string FileName { get; }
}
}

8
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidationContext.cs

@ -16,20 +16,20 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
public sealed class ValidationContext public sealed class ValidationContext
{ {
private readonly Func<IEnumerable<Guid>, Guid, Task<IReadOnlyList<Guid>>> checkContent; private readonly Func<IEnumerable<Guid>, Guid, Task<IReadOnlyList<Guid>>> checkContent;
private readonly Func<IEnumerable<Guid>, Task<IReadOnlyList<Guid>>> checkAsset; private readonly Func<IEnumerable<Guid>, Task<IReadOnlyList<IAssetInfo>>> checkAsset;
public bool IsOptional { get; } public bool IsOptional { get; }
public ValidationContext( public ValidationContext(
Func<IEnumerable<Guid>, Guid, Task<IReadOnlyList<Guid>>> checkContent, Func<IEnumerable<Guid>, Guid, Task<IReadOnlyList<Guid>>> checkContent,
Func<IEnumerable<Guid>, Task<IReadOnlyList<Guid>>> checkAsset) Func<IEnumerable<Guid>, Task<IReadOnlyList<IAssetInfo>>> checkAsset)
: this(checkContent, checkAsset, false) : this(checkContent, checkAsset, false)
{ {
} }
private ValidationContext( private ValidationContext(
Func<IEnumerable<Guid>, Guid, Task<IReadOnlyList<Guid>>> checkContent, Func<IEnumerable<Guid>, Guid, Task<IReadOnlyList<Guid>>> checkContent,
Func<IEnumerable<Guid>, Task<IReadOnlyList<Guid>>> checkAsset, Func<IEnumerable<Guid>, Task<IReadOnlyList<IAssetInfo>>> checkAsset,
bool isOptional) bool isOptional)
{ {
Guard.NotNull(checkAsset, nameof(checkAsset)); Guard.NotNull(checkAsset, nameof(checkAsset));
@ -51,7 +51,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
return checkContent(contentIds, schemaId); return checkContent(contentIds, schemaId);
} }
public Task<IReadOnlyList<Guid>> GetInvalidAssetIdsAsync(IEnumerable<Guid> assetId) public Task<IReadOnlyList<IAssetInfo>> GetAssetInfosAsync(IEnumerable<Guid> assetId)
{ {
return checkAsset(assetId); return checkAsset(assetId);
} }

96
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/AssetsValidator.cs

@ -8,21 +8,111 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.ValidateContent.Validators namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
{ {
public sealed class AssetsValidator : IValidator public sealed class AssetsValidator : IValidator
{ {
private readonly AssetsFieldProperties properties;
public AssetsValidator(AssetsFieldProperties properties)
{
this.properties = properties;
}
public async Task ValidateAsync(object value, ValidationContext context, Action<string> addError) public async Task ValidateAsync(object value, ValidationContext context, Action<string> addError)
{ {
if (value is ICollection<Guid> assetIds) if (value is ICollection<Guid> assetIds)
{ {
var invalidIds = await context.GetInvalidAssetIdsAsync(assetIds); var assets = await context.GetAssetInfosAsync(assetIds);
var i = 0;
foreach (var invalidId in invalidIds) foreach (var assetId in assetIds)
{ {
addError($"<FIELD> contains invalid asset '{invalidId}'."); i++;
var asset = assets.FirstOrDefault(x => x.AssetId == assetId);
void Error(string message)
{
addError($"<FIELD> has invalid asset #{i}: {message}");
}
if (asset == null)
{
Error($"Id '{assetId}' not found.");
continue;
}
if (properties.MinSize.HasValue && asset.FileSize < properties.MinSize)
{
Error($"'{asset.FileSize.ToReadableSize()}' less than minimum of '{properties.MinSize.Value.ToReadableSize()}'.");
}
if (properties.MaxSize.HasValue && asset.FileSize > properties.MaxSize)
{
Error($"'{asset.FileSize.ToReadableSize()}' greater than maximum of '{properties.MaxSize.Value.ToReadableSize()}'.");
}
if (properties.AllowedExtensions != null &&
properties.AllowedExtensions.Count > 0 &&
!properties.AllowedExtensions.Any(x => asset.FileName.EndsWith("." + x, StringComparison.OrdinalIgnoreCase)))
{
Error("Invalid file extension.");
}
if (!asset.IsImage)
{
if (properties.MustBeImage)
{
Error("Not an image.");
}
continue;
}
if (asset.PixelWidth.HasValue &&
asset.PixelHeight.HasValue)
{
var w = asset.PixelWidth.Value;
var h = asset.PixelHeight.Value;
var actualRatio = (double)w / h;
if (properties.MinWidth.HasValue && w < properties.MinWidth)
{
Error($"Width '{w}px' less than minimum of '{properties.MinWidth}px'.");
}
if (properties.MaxWidth.HasValue && w > properties.MaxWidth)
{
Error($"Width '{w}px' greater than maximum of '{properties.MaxWidth}px'.");
}
if (properties.MinHeight.HasValue && h < properties.MinHeight)
{
Error($"Height '{h}px' less than minimum of '{properties.MinHeight}px'.");
}
if (properties.MaxHeight.HasValue && h > properties.MaxHeight)
{
Error($"Height '{h}px' greater than maximum of '{properties.MaxHeight}px'.");
}
if (properties.AspectHeight.HasValue && properties.AspectWidth.HasValue)
{
var expectedRatio = (double)properties.AspectWidth.Value / properties.AspectHeight.Value;
if (Math.Abs(expectedRatio - actualRatio) > double.Epsilon)
{
Error($"Aspect ratio not '{properties.AspectWidth}:{properties.AspectHeight}'.");
}
}
}
} }
} }
} }

2
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidatorsFactory.cs

@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
yield return new CollectionValidator<Guid>(properties.IsRequired, properties.MinItems, properties.MaxItems); yield return new CollectionValidator<Guid>(properties.IsRequired, properties.MinItems, properties.MaxItems);
} }
yield return new AssetsValidator(); yield return new AssetsValidator(properties);
} }
public IEnumerable<IValidator> Visit(BooleanFieldProperties properties) public IEnumerable<IValidator> Visit(BooleanFieldProperties properties)

6
src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetEntity.cs

@ -8,6 +8,7 @@
using System; using System;
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Domain.Apps.Read.Assets; using Squidex.Domain.Apps.Read.Assets;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb;
@ -65,5 +66,10 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Assets
[BsonRequired] [BsonRequired]
[BsonElement] [BsonElement]
public RefToken LastModifiedBy { get; set; } public RefToken LastModifiedBy { get; set; }
Guid IAssetInfo.AssetId
{
get { return Id; }
}
} }
} }

9
src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetRepository.cs

@ -39,15 +39,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Assets
.Descending(x => x.LastModified)); .Descending(x => x.LastModified));
} }
public async Task<IReadOnlyList<Guid>> QueryNotFoundAsync(Guid appId, IList<Guid> assetIds)
{
var assetEntities =
await Collection.Find(x => assetIds.Contains(x.Id) && x.AppId == appId).Project<BsonDocument>(Project.Include(x => x.Id))
.ToListAsync();
return assetIds.Except(assetEntities.Select(x => Guid.Parse(x["_id"].AsString))).ToList();
}
public async Task<IReadOnlyList<IAssetEntity>> QueryAsync(Guid appId, HashSet<string> mimeTypes = null, HashSet<Guid> ids = null, string query = null, int take = 10, int skip = 0) public async Task<IReadOnlyList<IAssetEntity>> QueryAsync(Guid appId, HashSet<string> mimeTypes = null, HashSet<Guid> ids = null, string query = null, int take = 10, int skip = 0)
{ {
var filter = CreateFilter(appId, mimeTypes, ids, query); var filter = CreateFilter(appId, mimeTypes, ids, query);

1
src/Squidex.Domain.Apps.Read.MongoDb/History/MongoHistoryEventEntity.cs

@ -15,6 +15,7 @@ using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Read.MongoDb.History namespace Squidex.Domain.Apps.Read.MongoDb.History
{ {
public sealed class MongoHistoryEventEntity : MongoEntity, public sealed class MongoHistoryEventEntity : MongoEntity,
IEntity,
IEntityWithAppRef, IEntityWithAppRef,
IUpdateableEntityWithVersion, IUpdateableEntityWithVersion,
IUpdateableEntityWithCreatedBy, IUpdateableEntityWithCreatedBy,

20
src/Squidex.Domain.Apps.Read/Assets/IAssetEntity.cs

@ -6,22 +6,20 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using Squidex.Domain.Apps.Core.ValidateContent;
namespace Squidex.Domain.Apps.Read.Assets namespace Squidex.Domain.Apps.Read.Assets
{ {
public interface IAssetEntity : IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion public interface IAssetEntity :
IEntity,
IEntityWithAppRef,
IEntityWithCreatedBy,
IEntityWithLastModifiedBy,
IEntityWithVersion,
IAssetInfo
{ {
string MimeType { get; } string MimeType { get; }
string FileName { get; }
long FileSize { get; }
long FileVersion { get; } long FileVersion { get; }
bool IsImage { get; }
int? PixelWidth { get; }
int? PixelHeight { get; }
} }
} }

2
src/Squidex.Domain.Apps.Read/Assets/Repositories/IAssetRepository.cs

@ -16,8 +16,6 @@ namespace Squidex.Domain.Apps.Read.Assets.Repositories
{ {
Task<IReadOnlyList<IAssetEntity>> QueryAsync(Guid appId, HashSet<string> mimeTypes = null, HashSet<Guid> ids = null, string query = null, int take = 10, int skip = 0); Task<IReadOnlyList<IAssetEntity>> QueryAsync(Guid appId, HashSet<string> mimeTypes = null, HashSet<Guid> ids = null, string query = null, int take = 10, int skip = 0);
Task<IReadOnlyList<Guid>> QueryNotFoundAsync(Guid appId, IList<Guid> assetIds);
Task<IAssetEntity> FindAssetAsync(Guid id); Task<IAssetEntity> FindAssetAsync(Guid id);
Task<long> CountAsync(Guid appId, HashSet<string> mimeTypes = null, HashSet<Guid> ids = null, string query = null); Task<long> CountAsync(Guid appId, HashSet<string> mimeTypes = null, HashSet<Guid> ids = null, string query = null);

7
src/Squidex.Domain.Apps.Read/Contents/IContentEntity.cs

@ -11,7 +11,12 @@ using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Read.Contents namespace Squidex.Domain.Apps.Read.Contents
{ {
public interface IContentEntity : IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion public interface IContentEntity :
IEntity,
IEntityWithAppRef,
IEntityWithCreatedBy,
IEntityWithLastModifiedBy,
IEntityWithVersion
{ {
Status Status { get; } Status Status { get; }

16
src/Squidex.Domain.Apps.Read/Contents/QueryContext.cs

@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Read.Contents
if (asset == null) if (asset == null)
{ {
asset = await assetRepository.FindAssetAsync(id).ConfigureAwait(false); asset = await assetRepository.FindAssetAsync(id);
if (asset != null) if (asset != null)
{ {
@ -70,7 +70,7 @@ namespace Squidex.Domain.Apps.Read.Contents
if (content == null) if (content == null)
{ {
content = (await contentQuery.FindContentAsync(app, schemaId.ToString(), user, id).ConfigureAwait(false)).Content; content = (await contentQuery.FindContentAsync(app, schemaId.ToString(), user, id)).Content;
if (content != null) if (content != null)
{ {
@ -95,14 +95,14 @@ namespace Squidex.Domain.Apps.Read.Contents
public async Task<IReadOnlyList<IContentEntity>> QueryContentsAsync(string schemaIdOrName, string query) public async Task<IReadOnlyList<IContentEntity>> QueryContentsAsync(string schemaIdOrName, string query)
{ {
var contents = (await contentQuery.QueryWithCountAsync(app, schemaIdOrName, user, false, query).ConfigureAwait(false)).Items; var contents = await contentQuery.QueryWithCountAsync(app, schemaIdOrName, user, false, query);
foreach (var content in contents) foreach (var content in contents.Items)
{ {
cachedContents[content.Id] = content; cachedContents[content.Id] = content;
} }
return contents; return contents.Items;
} }
public async Task<IReadOnlyList<IAssetEntity>> GetReferencedAssetsAsync(ICollection<Guid> ids) public async Task<IReadOnlyList<IAssetEntity>> GetReferencedAssetsAsync(ICollection<Guid> ids)
@ -113,7 +113,7 @@ namespace Squidex.Domain.Apps.Read.Contents
if (notLoadedAssets.Count > 0) if (notLoadedAssets.Count > 0)
{ {
var assets = await assetRepository.QueryAsync(app.Id, null, notLoadedAssets, null, int.MaxValue).ConfigureAwait(false); var assets = await assetRepository.QueryAsync(app.Id, null, notLoadedAssets, null, int.MaxValue);
foreach (var asset in assets) foreach (var asset in assets)
{ {
@ -132,9 +132,9 @@ namespace Squidex.Domain.Apps.Read.Contents
if (notLoadedContents.Count > 0) if (notLoadedContents.Count > 0)
{ {
var contents = (await contentQuery.QueryWithCountAsync(app, schemaId.ToString(), user, false, notLoadedContents).ConfigureAwait(false)).Items; var contents = await contentQuery.QueryWithCountAsync(app, schemaId.ToString(), user, false, notLoadedContents);
foreach (var content in contents) foreach (var content in contents.Items)
{ {
cachedContents[content.Id] = content; cachedContents[content.Id] = content;
} }

4
src/Squidex.Domain.Apps.Read/History/IHistoryEventEntity.cs

@ -15,10 +15,10 @@ namespace Squidex.Domain.Apps.Read.History
{ {
Guid EventId { get; } Guid EventId { get; }
RefToken Actor { get; }
string Message { get; } string Message { get; }
long Version { get; } long Version { get; }
RefToken Actor { get; }
} }
} }

2
src/Squidex.Domain.Apps.Read/IEntityWithAppRef.cs

@ -10,7 +10,7 @@ using System;
namespace Squidex.Domain.Apps.Read namespace Squidex.Domain.Apps.Read
{ {
public interface IEntityWithAppRef : IEntity public interface IEntityWithAppRef
{ {
Guid AppId { get; } Guid AppId { get; }
} }

7
src/Squidex.Domain.Apps.Read/Rules/IRuleEntity.cs

@ -10,7 +10,12 @@ using Squidex.Domain.Apps.Core.Rules;
namespace Squidex.Domain.Apps.Read.Rules namespace Squidex.Domain.Apps.Read.Rules
{ {
public interface IRuleEntity : IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion public interface IRuleEntity :
IEntity,
IEntityWithAppRef,
IEntityWithCreatedBy,
IEntityWithLastModifiedBy,
IEntityWithVersion
{ {
Rule RuleDef { get; } Rule RuleDef { get; }
} }

7
src/Squidex.Domain.Apps.Read/Schemas/ISchemaEntity.cs

@ -10,7 +10,12 @@ using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Read.Schemas namespace Squidex.Domain.Apps.Read.Schemas
{ {
public interface ISchemaEntity : IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion public interface ISchemaEntity :
IEntity,
IEntityWithAppRef,
IEntityWithCreatedBy,
IEntityWithLastModifiedBy,
IEntityWithVersion
{ {
string Name { get; } string Name { get; }

14
src/Squidex.Domain.Apps.Write/Contents/ContentOperationContext.cs

@ -84,11 +84,11 @@ namespace Squidex.Domain.Apps.Write.Contents
new ValidationContext( new ValidationContext(
(contentIds, schemaId) => (contentIds, schemaId) =>
{ {
return contentRepository.QueryNotFoundAsync(appId, schemaId, contentIds.ToList()); return QueryContentsAsync(appId, schemaId, contentIds);
}, },
assetIds => assetIds =>
{ {
return assetRepository.QueryNotFoundAsync(appId, assetIds.ToList()); return QueryAssetsAsync(appId, assetIds);
}); });
if (partial) if (partial)
@ -107,6 +107,16 @@ namespace Squidex.Domain.Apps.Write.Contents
} }
} }
private async Task<IReadOnlyList<IAssetInfo>> QueryAssetsAsync(Guid appId, IEnumerable<Guid> assetIds)
{
return await assetRepository.QueryAsync(appId, null, new HashSet<Guid>(assetIds), null, int.MaxValue, 0);
}
private async Task<IReadOnlyList<Guid>> QueryContentsAsync(Guid appId, Guid schemaId, IEnumerable<Guid> contentIds)
{
return await contentRepository.QueryNotFoundAsync(appId, schemaId, contentIds.ToList());
}
public Task ExecuteScriptAndTransformAsync(Func<ISchemaEntity, string> script, object operation) public Task ExecuteScriptAndTransformAsync(Func<ISchemaEntity, string> script, object operation)
{ {
if (command is ContentDataCommand dataCommand) if (command is ContentDataCommand dataCommand)

28
src/Squidex.Domain.Apps.Write/Schemas/Guards/FieldPropertiesValidator.cs

@ -34,6 +34,34 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards
nameof(properties.MinItems), nameof(properties.MinItems),
nameof(properties.MaxItems)); nameof(properties.MaxItems));
} }
if (properties.MaxHeight.HasValue && properties.MinHeight.HasValue && properties.MinHeight.Value >= properties.MaxHeight.Value)
{
yield return new ValidationError("Max height must be greater than min height.",
nameof(properties.MaxHeight),
nameof(properties.MinHeight));
}
if (properties.MaxWidth.HasValue && properties.MinWidth.HasValue && properties.MinWidth.Value >= properties.MaxWidth.Value)
{
yield return new ValidationError("Max width must be greater than min width.",
nameof(properties.MaxWidth),
nameof(properties.MinWidth));
}
if (properties.MaxSize.HasValue && properties.MinSize.HasValue && properties.MinSize.Value >= properties.MaxSize.Value)
{
yield return new ValidationError("Max size must be greater than min size.",
nameof(properties.MaxSize),
nameof(properties.MinSize));
}
if (properties.AspectWidth.HasValue != properties.AspectHeight.HasValue)
{
yield return new ValidationError("Aspect width and height must be defined.",
nameof(properties.AspectWidth),
nameof(properties.AspectHeight));
}
} }
public IEnumerable<ValidationError> Visit(BooleanFieldProperties properties) public IEnumerable<ValidationError> Visit(BooleanFieldProperties properties)

5
src/Squidex.Infrastructure/FileExtensions.cs

@ -36,6 +36,11 @@ namespace Squidex.Infrastructure
} }
} }
public static string ToReadableSize(this int value)
{
return ToReadableSize((long)value);
}
public static string ToReadableSize(this long value) public static string ToReadableSize(this long value)
{ {
if (value < 0) if (value < 0)

12
src/Squidex.Infrastructure/States/StateFactory.cs

@ -88,25 +88,25 @@ namespace Squidex.Infrastructure.States
lock (lockObject) lock (lockObject)
{ {
if (statesCache.TryGetValue<T>(key, out var state)) if (statesCache.TryGetValue<ObjectHolder<T, TState>>(key, out var stateObj))
{ {
return Task.FromResult(state); return stateObj.ActivateAsync();
} }
state = (T)services.GetService(typeof(T)); var state = (T)services.GetService(typeof(T));
var stateHolder = new StateHolder<TState>(key, () => var stateHolder = new StateHolder<TState>(key, () =>
{ {
pubSub.Publish(new InvalidateMessage { Key = key }, false); pubSub.Publish(new InvalidateMessage { Key = key }, false);
}, store); }, store);
stateObj = new ObjectHolder<T, TState>(state, stateHolder);
statesCache.CreateEntry(key) statesCache.CreateEntry(key)
.SetValue(state) .SetValue(stateObj)
.SetAbsoluteExpiration(CacheDuration) .SetAbsoluteExpiration(CacheDuration)
.Dispose(); .Dispose();
var stateObj = new ObjectHolder<T, TState>(state, stateHolder);
return stateObj.ActivateAsync(); return stateObj.ActivateAsync();
} }
} }

24
src/Squidex/Areas/Api/Controllers/Schemas/Models/Converters/FieldPropertiesDtoFactory.cs

@ -26,11 +26,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Converters
return properties.Accept(Instance); return properties.Accept(Instance);
} }
public FieldPropertiesDto Visit(AssetsFieldProperties properties)
{
return SimpleMapper.Map(properties, new AssetsFieldPropertiesDto());
}
public FieldPropertiesDto Visit(BooleanFieldProperties properties) public FieldPropertiesDto Visit(BooleanFieldProperties properties)
{ {
return SimpleMapper.Map(properties, new BooleanFieldPropertiesDto()); return SimpleMapper.Map(properties, new BooleanFieldPropertiesDto());
@ -61,14 +56,20 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Converters
return SimpleMapper.Map(properties, new TagsFieldPropertiesDto()); return SimpleMapper.Map(properties, new TagsFieldPropertiesDto());
} }
public FieldPropertiesDto Visit(AssetsFieldProperties properties)
{
var result = SimpleMapper.Map(properties, new AssetsFieldPropertiesDto());
result.AllowedExtensions = properties.AllowedExtensions?.ToArray();
return result;
}
public FieldPropertiesDto Visit(NumberFieldProperties properties) public FieldPropertiesDto Visit(NumberFieldProperties properties)
{ {
var result = SimpleMapper.Map(properties, new NumberFieldPropertiesDto()); var result = SimpleMapper.Map(properties, new NumberFieldPropertiesDto());
if (properties.AllowedValues != null) result.AllowedValues = properties.AllowedValues?.ToArray();
{
result.AllowedValues = properties.AllowedValues.ToArray();
}
return result; return result;
} }
@ -77,10 +78,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Converters
{ {
var result = SimpleMapper.Map(properties, new StringFieldPropertiesDto()); var result = SimpleMapper.Map(properties, new StringFieldPropertiesDto());
if (properties.AllowedValues != null) result.AllowedValues = properties.AllowedValues?.ToArray();
{
result.AllowedValues = properties.AllowedValues.ToArray();
}
return result; return result;
} }

60
src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/AssetsFieldPropertiesDto.cs

@ -6,6 +6,7 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System.Collections.Immutable;
using NJsonSchema.Annotations; using NJsonSchema.Annotations;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
@ -25,9 +26,66 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
/// </summary> /// </summary>
public int? MaxItems { get; set; } public int? MaxItems { get; set; }
/// <summary>
/// The minimum file size in bytes.
/// </summary>
public int? MinSize { get; set; }
/// <summary>
/// The maximum file size in bytes.
/// </summary>
public int? MaxSize { get; set; }
/// <summary>
/// The minimum image width in pixels.
/// </summary>
public int? MinWidth { get; set; }
/// <summary>
/// The maximum image width in pixels.
/// </summary>
public int? MaxWidth { get; set; }
/// <summary>
/// The minimum image height in pixels.
/// </summary>
public int? MinHeight { get; set; }
/// <summary>
/// The maximum image height in pixels.
/// </summary>
public int? MaxHeight { get; set; }
/// <summary>
/// The image aspect width in pixels.
/// </summary>
public int? AspectWidth { get; set; }
/// <summary>
/// The image aspect height in pixels.
/// </summary>
public int? AspectHeight { get; set; }
/// <summary>
/// Defines if the asset must be an image.
/// </summary>
public bool MustBeImage { get; set; }
/// <summary>
/// The allowed file extensions.
/// </summary>
public string[] AllowedExtensions { get; set; }
public override FieldProperties ToProperties() public override FieldProperties ToProperties()
{ {
return SimpleMapper.Map(this, new AssetsFieldProperties()); var result = SimpleMapper.Map(this, new AssetsFieldProperties());
if (AllowedExtensions != null)
{
result.AllowedExtensions = ImmutableList.Create(AllowedExtensions);
}
return result;
} }
} }
} }

4
src/Squidex/app/features/administration/pages/users/user-page.component.html

@ -32,9 +32,7 @@
<div class="panel-main"> <div class="panel-main">
<div class="panel-content panel-content-blank"> <div class="panel-content panel-content-blank">
<div *ngIf="userFormError"> <div *ngIf="userFormError">
<div class="form-alert form-alert-error"> <div class="form-alert form-alert-error" [innerHTML]="userFormError"></div>
{{userFormError}}
</div>
</div> </div>
<div class="form-group"> <div class="form-group">

4
src/Squidex/app/features/schemas/pages/schema/field.component.html

@ -74,8 +74,8 @@
</ul> </ul>
<div class="float-right"> <div class="float-right">
<button [disabled]="editFormSubmitted || field.isLocked" type="reset" class="btn btn-secondary" (click)="cancel()">Cancel</button> <button [disabled]="field.isLocked" type="reset" class="btn btn-secondary" (click)="cancel()">Cancel</button>
<button [disabled]="editFormSubmitted || field.isLocked" type="submit" class="btn btn-primary ml-1">Save</button> <button [disabled]="field.isLocked" type="submit" class="btn btn-primary ml-1">Save</button>
</div> </div>
</div> </div>

90
src/Squidex/app/features/schemas/pages/schema/types/assets-validation.component.html

@ -10,13 +10,95 @@
<div class="form-group row"> <div class="form-group row">
<label class="col col-3 col-form-label">Items</label> <label class="col col-3 col-form-label">Items</label>
<div class="col col-3 minlength-col"> <div class="col col-3 minmax-col">
<input type="number" class="form-control" id="field-min-items" formControlName="minItems" placeholder="Min Items" /> <input type="number" class="form-control" id="field-min-items" formControlName="minItems" placeholder="Min Assets" />
<label class="col-form-label minitems-label">-</label> <label class="col-form-label minmax-label">-</label>
</div> </div>
<div class="col col-3"> <div class="col col-3">
<input type="number" class="form-control" id="field-max-items" formControlName="maxItems" placeholder="Max Items" /> <input type="number" class="form-control" id="field-max-items" formControlName="maxItems" placeholder="Max Assets" />
</div>
</div>
<div class="form-group row">
<label class="col col-3 col-form-label">Size</label>
<div class="col col-3 minmax-col">
<input type="number" class="form-control" id="field-min-size" formControlName="minSize" placeholder="Min Size" />
<label class="col-form-label minmax-label">-</label>
</div>
<div class="col col-3">
<input type="number" class="form-control" id="field-max-size" formControlName="maxSize" placeholder="Max Size" />
</div>
<div class="col col-3">
<label class="col-form-label">bytes</label>
</div>
</div>
<div class="form-group2 row">
<label class="col col-3 col-form-checkbox-label" for="field-must-be-image">Must be Image</label>
<div class="col col-6">
<input type="checkbox" class="form-check-input" id="field-must-be-image" formControlName="mustBeImage" />
</div>
</div>
<div class="form-group row">
<label class="col col-3 col-form-label">Width</label>
<div class="col col-2 minmax-col">
<input type="number" class="form-control" id="field-min-width" formControlName="minWidth" />
<label class="col-form-label minmax-label">-</label>
</div>
<div class="col col-2">
<input type="number" class="form-control" id="field-max-width" formControlName="maxWidth" />
</div>
<div class="col col-2">
<label class="col-form-label">px</label>
</div>
</div>
<div class="form-group row">
<label class="col col-3 col-form-label">Height</label>
<div class="col col-2 minmax-col">
<input type="number" class="form-control" id="field-min-height" formControlName="minHeight" />
<label class="col-form-label minmax-label">-</label>
</div>
<div class="col col-2">
<input type="number" class="form-control" id="field-max-height" formControlName="maxHeight" />
</div>
<div class="col col-2">
<label class="col-form-label">px</label>
</div>
</div>
<div class="form-group row">
<label class="col col-3 col-form-label">AspectRatio</label>
<div class="col col-2 minmax-col">
<input type="number" class="form-control" id="field-aspect-height" formControlName="aspectWidth" placeholder="4" />
<label class="col-form-label minmax-label">:</label>
</div>
<div class="col col-2">
<input type="number" class="form-control" id="field-aspect-height" formControlName="aspectHeight" placeholder="3" />
</div>
<div class="col col-2">
<label class="col-form-label">px</label>
</div>
</div>
<div class="form-group2 row">
<label for="field-allowed-values" class="col col-3 col-form-label">
Allowed Extensions
</label>
<div class="col col-6">
<sqx-tag-editor formControlName="allowedExtensions"></sqx-tag-editor>
</div> </div>
</div> </div>
</div> </div>

6
src/Squidex/app/features/schemas/pages/schema/types/assets-validation.component.scss

@ -1,7 +1,7 @@
@import '_vars'; @import '_vars';
@import '_mixins'; @import '_mixins';
.minitems { .minmax {
&-col { &-col {
position: relative; position: relative;
} }
@ -17,4 +17,8 @@
.form-group { .form-group {
margin-top: .5rem; margin-top: .5rem;
}
.form-group2 {
margin-top: 3rem;
} }

34
src/Squidex/app/features/schemas/pages/schema/types/assets-validation.component.ts

@ -24,10 +24,40 @@ export class AssetsValidationComponent implements OnInit {
public properties: AssetsFieldPropertiesDto; public properties: AssetsFieldPropertiesDto;
public ngOnInit() { public ngOnInit() {
this.editForm.setControl('minItems',
new FormControl(this.properties.minItems));
this.editForm.setControl('maxItems', this.editForm.setControl('maxItems',
new FormControl(this.properties.maxItems)); new FormControl(this.properties.maxItems));
this.editForm.setControl('minItems', this.editForm.setControl('minSize',
new FormControl(this.properties.minItems)); new FormControl(this.properties.minSize));
this.editForm.setControl('maxSize',
new FormControl(this.properties.maxSize));
this.editForm.setControl('allowedExtensions',
new FormControl(this.properties.allowedExtensions));
this.editForm.setControl('mustBeImage',
new FormControl(this.properties.mustBeImage));
this.editForm.setControl('minWidth',
new FormControl(this.properties.minWidth));
this.editForm.setControl('maxWidth',
new FormControl(this.properties.maxWidth));
this.editForm.setControl('minHeight',
new FormControl(this.properties.minHeight));
this.editForm.setControl('maxHeight',
new FormControl(this.properties.maxHeight));
this.editForm.setControl('aspectWidth',
new FormControl(this.properties.aspectWidth));
this.editForm.setControl('aspectHeight',
new FormControl(this.properties.aspectHeight));
} }
} }

4
src/Squidex/app/features/schemas/pages/schema/types/number-validation.component.html

@ -10,10 +10,10 @@
<div class="form-group row"> <div class="form-group row">
<label class="col col-3 col-form-label">Range</label> <label class="col col-3 col-form-label">Range</label>
<div class="col col-3 minlength-col"> <div class="col col-3 minmax-col">
<input type="number" class="form-control" id="field-min-value" formControlName="minValue" placeholder="Min Value" /> <input type="number" class="form-control" id="field-min-value" formControlName="minValue" placeholder="Min Value" />
<label class="col-form-label minlength-label">-</label> <label class="col-form-label minmax-label">-</label>
</div> </div>
<div class="col col-3"> <div class="col col-3">
<input type="number" class="form-control" id="field-max-value" formControlName="maxValue" placeholder="Max Value" /> <input type="number" class="form-control" id="field-max-value" formControlName="maxValue" placeholder="Max Value" />

2
src/Squidex/app/features/schemas/pages/schema/types/number-validation.component.scss

@ -1,7 +1,7 @@
@import '_vars'; @import '_vars';
@import '_mixins'; @import '_mixins';
.minlength { .minmax {
&-col { &-col {
position: relative; position: relative;
} }

4
src/Squidex/app/features/schemas/pages/schema/types/references-validation.component.html

@ -20,10 +20,10 @@
<div class="form-group row"> <div class="form-group row">
<label class="col col-3 col-form-label">Items</label> <label class="col col-3 col-form-label">Items</label>
<div class="col col-3 minlength-col"> <div class="col col-3 minmax-col">
<input type="number" class="form-control" id="field-min-items" formControlName="minItems" placeholder="Min Items" /> <input type="number" class="form-control" id="field-min-items" formControlName="minItems" placeholder="Min Items" />
<label class="col-form-label minitems-label">-</label> <label class="col-form-label minmax-label">-</label>
</div> </div>
<div class="col col-3"> <div class="col col-3">
<input type="number" class="form-control" id="field-max-items" formControlName="maxItems" placeholder="Max Items" /> <input type="number" class="form-control" id="field-max-items" formControlName="maxItems" placeholder="Max Items" />

2
src/Squidex/app/features/schemas/pages/schema/types/references-validation.component.scss

@ -1,7 +1,7 @@
@import '_vars'; @import '_vars';
@import '_mixins'; @import '_mixins';
.minitems { .minmax {
&-col { &-col {
position: relative; position: relative;
} }

4
src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.html

@ -10,10 +10,10 @@
<div class="form-group row"> <div class="form-group row">
<label class="col col-3 col-form-label">Length</label> <label class="col col-3 col-form-label">Length</label>
<div class="col col-3 minlength-col"> <div class="col col-3 minmax-col">
<input type="number" class="form-control" id="field-min-length" formControlName="minLength" placeholder="Min Length" /> <input type="number" class="form-control" id="field-min-length" formControlName="minLength" placeholder="Min Length" />
<label class="col-form-label minlength-label">-</label> <label class="col-form-label minmax-label">-</label>
</div> </div>
<div class="col col-3"> <div class="col col-3">
<input type="number" class="form-control" id="field-max-length" formControlName="maxLength" placeholder="Max Length" /> <input type="number" class="form-control" id="field-max-length" formControlName="maxLength" placeholder="Max Length" />

2
src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.scss

@ -1,7 +1,7 @@
@import '_vars'; @import '_vars';
@import '_mixins'; @import '_mixins';
.minlength { .minmax {
&-col { &-col {
position: relative; position: relative;
} }

4
src/Squidex/app/features/schemas/pages/schema/types/tags-validation.component.html

@ -10,10 +10,10 @@
<div class="form-group row"> <div class="form-group row">
<label class="col col-3 col-form-label">Items</label> <label class="col col-3 col-form-label">Items</label>
<div class="col col-3 minlength-col"> <div class="col col-3 minmax-col">
<input type="number" class="form-control" id="field-min-items" formControlName="minItems" placeholder="Min Items" /> <input type="number" class="form-control" id="field-min-items" formControlName="minItems" placeholder="Min Items" />
<label class="col-form-label minitems-label">-</label> <label class="col-form-label minmax-label">-</label>
</div> </div>
<div class="col col-3"> <div class="col col-3">
<input type="number" class="form-control" id="field-max-items" formControlName="maxItems" placeholder="Max Items" /> <input type="number" class="form-control" id="field-max-items" formControlName="maxItems" placeholder="Max Items" />

2
src/Squidex/app/features/schemas/pages/schema/types/tags-validation.component.scss

@ -1,7 +1,7 @@
@import '_vars'; @import '_vars';
@import '_mixins'; @import '_mixins';
.minitems { .minmax {
&-col { &-col {
position: relative; position: relative;
} }

4
src/Squidex/app/features/schemas/pages/schemas/schema-form.component.html

@ -1,8 +1,6 @@
<form [formGroup]="createForm" (ngSubmit)="createSchema()"> <form [formGroup]="createForm" (ngSubmit)="createSchema()">
<div *ngIf="createFormError"> <div *ngIf="createFormError">
<div class="form-alert form-alert-error"> <div class="form-alert form-alert-error" [innerHTML]="createFormError"></div>
{{createFormError}}
</div>
</div> </div>
<div class="form-group"> <div class="form-group">

2
src/Squidex/app/framework/angular/control-errors.component.ts

@ -16,7 +16,7 @@ const DEFAULT_ERRORS: { [key: string]: string } = {
patternmessage: '{message}', patternmessage: '{message}',
minvalue: '{field} must be larger than {minValue}.', minvalue: '{field} must be larger than {minValue}.',
maxvalue: '{field} must be smaller than {maxValue}.', maxvalue: '{field} must be smaller than {maxValue}.',
minlength: '{field} must have a length of more than {requiredLength}.', minmax: '{field} must have a length of more than {requiredLength}.',
maxlength: '{field} must have a length of less than {requiredLength}.', maxlength: '{field} must have a length of less than {requiredLength}.',
match: '{message}', match: '{message}',
validdatetime: '{field} is not a valid date time', validdatetime: '{field} is not a valid date time',

2
src/Squidex/app/framework/angular/dialog-renderer.component.html

@ -24,6 +24,6 @@
<div class="alert alert-dismissible alert-{{notification.messageType}}" *ngFor="let notification of notifications" (click)="close(notification)" @fade> <div class="alert alert-dismissible alert-{{notification.messageType}}" *ngFor="let notification of notifications" (click)="close(notification)" @fade>
<button type="button" class="close" data-dismiss="alert" (close)="close(notification)">&times;</button> <button type="button" class="close" data-dismiss="alert" (close)="close(notification)">&times;</button>
{{notification.message}} <span [innerHTML]="notification.message"></span>
</div> </div>
</div> </div>

8
src/Squidex/app/framework/angular/dialog-renderer.component.scss

@ -4,12 +4,16 @@
.notification-container { .notification-container {
& { & {
margin: .625rem; margin: .625rem;
max-width: 20rem; max-width: 30rem;
min-width: 20rem; min-width: 30rem;
position: fixed; position: fixed;
z-index: 100000; z-index: 100000;
} }
.alert {
max-height: 20rem;
}
&-topright { &-topright {
@include fixed(0, 0, auto, auto); @include fixed(0, 0, auto, auto);
} }

26
src/Squidex/app/framework/angular/http-extensions-impl.ts

@ -19,29 +19,29 @@ export class Versioned<T> {
} }
function formatMessage(message: string, details?: string[]) { function formatMessage(message: string, details?: string[]) {
const parts: string[] = []; const format = (row: string) => {
const last = row[row.length - 1];
const addPart = (p: string) => { if (last !== '.') {
p = p.trim(); return row + '.';
} else {
const c = p[p.length - 1]; return row;
if (c !== '.') {
p += '.';
} }
parts.push(p);
}; };
addPart(message); let result = format(message);
if (details) { if (details) {
result = result + '<ul>';
for (let d of details) { for (let d of details) {
addPart(d); result += `<li>${format(d)}</li>`;
} }
result = result + '</ul>';
} }
return parts.join(' '); return result;
} }
export class ErrorDto { export class ErrorDto {

12
src/Squidex/app/shared/services/schemas.service.ts

@ -579,7 +579,17 @@ export class AssetsFieldPropertiesDto extends FieldPropertiesDto {
isRequired: boolean, isRequired: boolean,
isListField: boolean, isListField: boolean,
public readonly minItems?: number, public readonly minItems?: number,
public readonly maxItems?: number public readonly maxItems?: number,
public readonly minSize?: number,
public readonly maxSize?: number,
public readonly allowedExtensions?: string[],
public readonly mustBeImage?: boolean,
public readonly minWidth?: number,
public readonly maxWidth?: number,
public readonly minHeight?: number,
public readonly maxHeight?: number,
public readonly aspectWidth?: number,
public readonly aspectHeight?: number
) { ) {
super('Assets', label, hints, placeholder, isRequired, isListField); super('Assets', label, hints, placeholder, isRequired, isListField);
} }

190
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs

@ -8,11 +8,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.ValidateContent;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
@ -21,22 +23,62 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
{ {
private readonly List<string> errors = new List<string>(); private readonly List<string> errors = new List<string>();
public sealed class AssetInfo : IAssetInfo
{
public Guid AssetId { get; set; }
public string FileName { get; set; }
public long FileSize { get; set; }
public bool IsImage { get; set; }
public int? PixelWidth { get; set; }
public int? PixelHeight { get; set; }
}
private readonly AssetInfo document = new AssetInfo
{
AssetId = Guid.NewGuid(),
FileName = "MyDocument.pdf",
FileSize = 1024 * 4,
IsImage = false,
PixelWidth = null,
PixelHeight = null
};
private readonly AssetInfo image = new AssetInfo
{
AssetId = Guid.NewGuid(),
FileName = "MyImage.png",
FileSize = 1024 * 8,
IsImage = true,
PixelWidth = 800,
PixelHeight = 600
};
private readonly ValidationContext ctx;
public AssetsFieldTests()
{
ctx = ValidationTestExtensions.Assets(image, document);
}
[Fact] [Fact]
public void Should_instantiate_field() public void Should_instantiate_field()
{ {
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant); var sut = Field(new AssetsFieldProperties());
Assert.Equal("my-asset", sut.Name); Assert.Equal("my-assets", sut.Name);
} }
[Fact] [Fact]
public async Task Should_not_add_error_if_assets_are_valid() public async Task Should_not_add_error_if_assets_are_valid()
{ {
var assetId = Guid.NewGuid(); var sut = Field(new AssetsFieldProperties());
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant);
await sut.ValidateAsync(CreateValue(assetId), errors); await sut.ValidateAsync(CreateValue(document.AssetId), errors, ctx);
Assert.Empty(errors); Assert.Empty(errors);
} }
@ -44,9 +86,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_not_add_error_if_assets_are_null_and_valid() public async Task Should_not_add_error_if_assets_are_null_and_valid()
{ {
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant); var sut = Field(new AssetsFieldProperties());
await sut.ValidateAsync(CreateValue(null), errors); await sut.ValidateAsync(CreateValue(null), errors, ctx);
Assert.Empty(errors); Assert.Empty(errors);
} }
@ -54,9 +96,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_assets_are_required_and_null() public async Task Should_add_errors_if_assets_are_required_and_null()
{ {
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant, new AssetsFieldProperties { IsRequired = true }); var sut = Field(new AssetsFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(null), errors); await sut.ValidateAsync(CreateValue(null), errors, ctx);
errors.ShouldBeEquivalentTo( errors.ShouldBeEquivalentTo(
new[] { "<FIELD> is required." }); new[] { "<FIELD> is required." });
@ -65,9 +107,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_assets_are_required_and_empty() public async Task Should_add_errors_if_assets_are_required_and_empty()
{ {
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant, new AssetsFieldProperties { IsRequired = true }); var sut = Field(new AssetsFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(), errors); await sut.ValidateAsync(CreateValue(), errors, ctx);
errors.ShouldBeEquivalentTo( errors.ShouldBeEquivalentTo(
new[] { "<FIELD> is required." }); new[] { "<FIELD> is required." });
@ -76,7 +118,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_value_is_not_valid() public async Task Should_add_errors_if_value_is_not_valid()
{ {
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant); var sut = Field(new AssetsFieldProperties());
await sut.ValidateAsync("invalid", errors); await sut.ValidateAsync("invalid", errors);
@ -87,9 +129,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_value_has_not_enough_items() public async Task Should_add_errors_if_value_has_not_enough_items()
{ {
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant, new AssetsFieldProperties { MinItems = 3 }); var sut = Field(new AssetsFieldProperties { MinItems = 3 });
await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors); await sut.ValidateAsync(CreateValue(document.AssetId, document.AssetId), errors, ctx);
errors.ShouldBeEquivalentTo( errors.ShouldBeEquivalentTo(
new[] { "<FIELD> must have at least 3 item(s)." }); new[] { "<FIELD> must have at least 3 item(s)." });
@ -98,9 +140,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_value_has_too_much_items() public async Task Should_add_errors_if_value_has_too_much_items()
{ {
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant, new AssetsFieldProperties { MaxItems = 1 }); var sut = Field(new AssetsFieldProperties { MaxItems = 1 });
await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors); await sut.ValidateAsync(CreateValue(document.AssetId, document.AssetId), errors, ctx);
errors.ShouldBeEquivalentTo( errors.ShouldBeEquivalentTo(
new[] { "<FIELD> must have not more than 1 item(s)." }); new[] { "<FIELD> must have not more than 1 item(s)." });
@ -111,17 +153,125 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
{ {
var assetId = Guid.NewGuid(); var assetId = Guid.NewGuid();
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant); var sut = Field(new AssetsFieldProperties());
await sut.ValidateAsync(CreateValue(assetId), errors, ctx);
errors.ShouldBeEquivalentTo(
new[] { $"<FIELD> has invalid asset #1: Id '{assetId}' not found." });
}
[Fact]
public async Task Should_add_error_if_document_is_too_small()
{
var sut = Field(new AssetsFieldProperties { MinSize = 5 * 1024 });
await sut.ValidateAsync(CreateValue(document.AssetId, image.AssetId), errors, ctx);
errors.ShouldBeEquivalentTo(
new[] { $"<FIELD> has invalid asset #1: '4 kB' less than minimum of '5 kB'." });
}
[Fact]
public async Task Should_add_error_if_document_is_too_big()
{
var sut = Field(new AssetsFieldProperties { MaxSize = 5 * 1024 });
await sut.ValidateAsync(CreateValue(document.AssetId, image.AssetId), errors, ctx);
errors.ShouldBeEquivalentTo(
new[] { $"<FIELD> has invalid asset #2: '8 kB' greater than maximum of '5 kB'." });
}
[Fact]
public async Task Should_add_error_if_document_is_not_an_image()
{
var sut = Field(new AssetsFieldProperties { MustBeImage = true });
await sut.ValidateAsync(CreateValue(document.AssetId, image.AssetId), errors, ctx);
errors.ShouldBeEquivalentTo(
new[] { $"<FIELD> has invalid asset #1: Not an image." });
}
[Fact]
public async Task Should_add_error_if_image_width_is_too_small()
{
var sut = Field(new AssetsFieldProperties { MinWidth = 1000 });
await sut.ValidateAsync(CreateValue(document.AssetId, image.AssetId), errors, ctx);
errors.ShouldBeEquivalentTo(
new[] { $"<FIELD> has invalid asset #2: Width '800px' less than minimum of '1000px'." });
}
[Fact]
public async Task Should_add_error_if_image_width_is_too_big()
{
var sut = Field(new AssetsFieldProperties { MaxWidth = 700 });
await sut.ValidateAsync(CreateValue(document.AssetId, image.AssetId), errors, ctx);
errors.ShouldBeEquivalentTo(
new[] { $"<FIELD> has invalid asset #2: Width '800px' greater than maximum of '700px'." });
}
[Fact]
public async Task Should_add_error_if_image_height_is_too_small()
{
var sut = Field(new AssetsFieldProperties { MinHeight = 800 });
await sut.ValidateAsync(CreateValue(document.AssetId, image.AssetId), errors, ctx);
errors.ShouldBeEquivalentTo(
new[] { $"<FIELD> has invalid asset #2: Height '600px' less than minimum of '800px'." });
}
[Fact]
public async Task Should_add_error_if_image_height_is_too_big()
{
var sut = Field(new AssetsFieldProperties { MaxHeight = 500 });
await sut.ValidateAsync(CreateValue(document.AssetId, image.AssetId), errors, ctx);
errors.ShouldBeEquivalentTo(
new[] { $"<FIELD> has invalid asset #2: Height '600px' greater than maximum of '500px'." });
}
[Fact]
public async Task Should_add_error_if_image_has_invalid_aspect_ratio()
{
var sut = Field(new AssetsFieldProperties { AspectWidth = 1, AspectHeight = 1 });
await sut.ValidateAsync(CreateValue(assetId), errors, ValidationTestExtensions.InvalidContext(assetId)); await sut.ValidateAsync(CreateValue(document.AssetId, image.AssetId), errors, ctx);
errors.ShouldBeEquivalentTo( errors.ShouldBeEquivalentTo(
new[] { $"<FIELD> contains invalid asset '{assetId}'." }); new[] { "<FIELD> has invalid asset #2: Aspect ratio not '1:1'." });
}
[Fact]
public async Task Should_add_error_if_image_has_invalid_extension()
{
var sut = Field(new AssetsFieldProperties { AllowedExtensions = ImmutableList.Create("mp4") });
await sut.ValidateAsync(CreateValue(document.AssetId, image.AssetId), errors, ctx);
errors.ShouldBeEquivalentTo(
new[]
{
$"<FIELD> has invalid asset #1: Invalid file extension.",
$"<FIELD> has invalid asset #2: Invalid file extension."
});
} }
private static JToken CreateValue(params Guid[] ids) private static JToken CreateValue(params Guid[] ids)
{ {
return ids == null ? JValue.CreateNull() : (JToken)new JArray(ids.OfType<object>().ToArray()); return ids == null ? JValue.CreateNull() : (JToken)new JArray(ids.OfType<object>().ToArray());
} }
private static AssetsField Field(AssetsFieldProperties properties)
{
return new AssetsField(1, "my-assets", Partitioning.Invariant, properties);
}
} }
} }

17
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/BooleanFieldTests.cs

@ -22,15 +22,15 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public void Should_instantiate_field() public void Should_instantiate_field()
{ {
var sut = new BooleanField(1, "my-bolean", Partitioning.Invariant); var sut = Field(new BooleanFieldProperties());
Assert.Equal("my-bolean", sut.Name); Assert.Equal("my-boolean", sut.Name);
} }
[Fact] [Fact]
public async Task Should_not_add_error_if_null_boolean_is_valid() public async Task Should_not_add_error_if_null_boolean_is_valid()
{ {
var sut = new BooleanField(1, "my-bolean", Partitioning.Invariant); var sut = Field(new BooleanFieldProperties());
await sut.ValidateAsync(CreateValue(null), errors); await sut.ValidateAsync(CreateValue(null), errors);
@ -40,7 +40,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_not_add_error_if_boolean_is_valid() public async Task Should_not_add_error_if_boolean_is_valid()
{ {
var sut = new BooleanField(1, "my-bolean", Partitioning.Invariant); var sut = Field(new BooleanFieldProperties());
await sut.ValidateAsync(CreateValue(true), errors); await sut.ValidateAsync(CreateValue(true), errors);
@ -50,7 +50,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_boolean_is_required() public async Task Should_add_errors_if_boolean_is_required()
{ {
var sut = new BooleanField(1, "my-bolean", Partitioning.Invariant, new BooleanFieldProperties { IsRequired = true }); var sut = Field(new BooleanFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(null), errors); await sut.ValidateAsync(CreateValue(null), errors);
@ -61,7 +61,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_value_is_not_valid() public async Task Should_add_errors_if_value_is_not_valid()
{ {
var sut = new BooleanField(1, "my-bolean", Partitioning.Invariant); var sut = Field(new BooleanFieldProperties());
await sut.ValidateAsync(CreateValue("Invalid"), errors); await sut.ValidateAsync(CreateValue("Invalid"), errors);
@ -73,5 +73,10 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
{ {
return new JValue(v); return new JValue(v);
} }
private static BooleanField Field(BooleanFieldProperties properties)
{
return new BooleanField(1, "my-boolean", Partitioning.Invariant, properties);
}
} }
} }

19
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/DateTimeFieldTests.cs

@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public void Should_instantiate_field() public void Should_instantiate_field()
{ {
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant); var sut = Field(new DateTimeFieldProperties());
Assert.Equal("my-datetime", sut.Name); Assert.Equal("my-datetime", sut.Name);
} }
@ -32,7 +32,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_not_add_error_if_datetime_is_valid() public async Task Should_not_add_error_if_datetime_is_valid()
{ {
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant); var sut = Field(new DateTimeFieldProperties());
await sut.ValidateAsync(CreateValue(null), errors); await sut.ValidateAsync(CreateValue(null), errors);
@ -42,7 +42,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_datetime_is_required() public async Task Should_add_errors_if_datetime_is_required()
{ {
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant, new DateTimeFieldProperties { IsRequired = true }); var sut = Field(new DateTimeFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(null), errors); await sut.ValidateAsync(CreateValue(null), errors);
@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_datetime_is_less_than_min() public async Task Should_add_errors_if_datetime_is_less_than_min()
{ {
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant, new DateTimeFieldProperties { MinValue = FutureDays(10) }); var sut = Field(new DateTimeFieldProperties { MinValue = FutureDays(10) });
await sut.ValidateAsync(CreateValue(FutureDays(0)), errors); await sut.ValidateAsync(CreateValue(FutureDays(0)), errors);
@ -64,7 +64,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_datetime_is_greater_than_max() public async Task Should_add_errors_if_datetime_is_greater_than_max()
{ {
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant, new DateTimeFieldProperties { MaxValue = FutureDays(10) }); var sut = Field(new DateTimeFieldProperties { MaxValue = FutureDays(10) });
await sut.ValidateAsync(CreateValue(FutureDays(20)), errors); await sut.ValidateAsync(CreateValue(FutureDays(20)), errors);
@ -75,7 +75,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_value_is_not_valid() public async Task Should_add_errors_if_value_is_not_valid()
{ {
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant); var sut = Field(new DateTimeFieldProperties());
await sut.ValidateAsync(CreateValue("Invalid"), errors); await sut.ValidateAsync(CreateValue("Invalid"), errors);
@ -86,7 +86,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_value_is_another_type() public async Task Should_add_errors_if_value_is_another_type()
{ {
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant); var sut = Field(new DateTimeFieldProperties());
await sut.ValidateAsync(CreateValue(123), errors); await sut.ValidateAsync(CreateValue(123), errors);
@ -103,5 +103,10 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
{ {
return v is Instant ? new JValue(v.ToString()) : new JValue(v); return v is Instant ? new JValue(v.ToString()) : new JValue(v);
} }
private static DateTimeField Field(DateTimeFieldProperties properties)
{
return new DateTimeField(1, "my-datetime", Partitioning.Invariant, properties);
}
} }
} }

19
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/GeolocationFieldTests.cs

@ -22,7 +22,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public void Should_instantiate_field() public void Should_instantiate_field()
{ {
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant); var sut = Field(new GeolocationFieldProperties());
Assert.Equal("my-geolocation", sut.Name); Assert.Equal("my-geolocation", sut.Name);
} }
@ -30,7 +30,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_not_add_error_if_geolocation_is_valid_null() public async Task Should_not_add_error_if_geolocation_is_valid_null()
{ {
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant); var sut = Field(new GeolocationFieldProperties());
await sut.ValidateAsync(CreateValue(JValue.CreateNull()), errors); await sut.ValidateAsync(CreateValue(JValue.CreateNull()), errors);
@ -40,7 +40,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_not_add_error_if_geolocation_is_valid() public async Task Should_not_add_error_if_geolocation_is_valid()
{ {
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant); var sut = Field(new GeolocationFieldProperties());
var geolocation = new JObject( var geolocation = new JObject(
new JProperty("latitude", 0), new JProperty("latitude", 0),
@ -54,7 +54,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_geolocation_has_invalid_latitude() public async Task Should_add_errors_if_geolocation_has_invalid_latitude()
{ {
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant, new GeolocationFieldProperties { IsRequired = true }); var sut = Field(new GeolocationFieldProperties { IsRequired = true });
var geolocation = new JObject( var geolocation = new JObject(
new JProperty("latitude", 200), new JProperty("latitude", 200),
@ -69,7 +69,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_geolocation_has_invalid_longitude() public async Task Should_add_errors_if_geolocation_has_invalid_longitude()
{ {
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant, new GeolocationFieldProperties { IsRequired = true }); var sut = Field(new GeolocationFieldProperties { IsRequired = true });
var geolocation = new JObject( var geolocation = new JObject(
new JProperty("latitude", 0), new JProperty("latitude", 0),
@ -84,7 +84,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_geolocation_has_too_many_properties() public async Task Should_add_errors_if_geolocation_has_too_many_properties()
{ {
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant, new GeolocationFieldProperties { IsRequired = true }); var sut = Field(new GeolocationFieldProperties { IsRequired = true });
var geolocation = new JObject( var geolocation = new JObject(
new JProperty("invalid", 0), new JProperty("invalid", 0),
@ -100,7 +100,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_geolocation_is_required() public async Task Should_add_errors_if_geolocation_is_required()
{ {
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant, new GeolocationFieldProperties { IsRequired = true }); var sut = Field(new GeolocationFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(JValue.CreateNull()), errors); await sut.ValidateAsync(CreateValue(JValue.CreateNull()), errors);
@ -112,5 +112,10 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
{ {
return v; return v;
} }
private static GeolocationField Field(GeolocationFieldProperties properties)
{
return new GeolocationField(1, "my-geolocation", Partitioning.Invariant, properties);
}
} }
} }

11
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/JsonFieldTests.cs

@ -22,7 +22,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public void Should_instantiate_field() public void Should_instantiate_field()
{ {
var sut = new JsonField(1, "my-json", Partitioning.Invariant); var sut = Field(new JsonFieldProperties());
Assert.Equal("my-json", sut.Name); Assert.Equal("my-json", sut.Name);
} }
@ -30,7 +30,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_not_add_error_if_json_is_valid() public async Task Should_not_add_error_if_json_is_valid()
{ {
var sut = new JsonField(1, "my-json", Partitioning.Invariant); var sut = Field(new JsonFieldProperties());
await sut.ValidateAsync(CreateValue(new JValue(1)), errors); await sut.ValidateAsync(CreateValue(new JValue(1)), errors);
@ -40,7 +40,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_json_is_required() public async Task Should_add_errors_if_json_is_required()
{ {
var sut = new JsonField(1, "my-json", Partitioning.Invariant, new JsonFieldProperties { IsRequired = true }); var sut = Field(new JsonFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(JValue.CreateNull()), errors); await sut.ValidateAsync(CreateValue(JValue.CreateNull()), errors);
@ -52,5 +52,10 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
{ {
return v; return v;
} }
private static JsonField Field(JsonFieldProperties properties)
{
return new JsonField(1, "my-json", Partitioning.Invariant, properties);
}
} }
} }

19
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/NumberFieldTests.cs

@ -23,7 +23,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public void Should_instantiate_field() public void Should_instantiate_field()
{ {
var sut = new NumberField(1, "my-number", Partitioning.Invariant); var sut = Field(new NumberFieldProperties());
Assert.Equal("my-number", sut.Name); Assert.Equal("my-number", sut.Name);
} }
@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_not_add_error_if_number_is_valid() public async Task Should_not_add_error_if_number_is_valid()
{ {
var sut = new NumberField(1, "my-number", Partitioning.Invariant); var sut = Field(new NumberFieldProperties());
await sut.ValidateAsync(CreateValue(null), errors); await sut.ValidateAsync(CreateValue(null), errors);
@ -41,7 +41,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_number_is_required() public async Task Should_add_errors_if_number_is_required()
{ {
var sut = new NumberField(1, "my-number", Partitioning.Invariant, new NumberFieldProperties { IsRequired = true }); var sut = Field(new NumberFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(null), errors); await sut.ValidateAsync(CreateValue(null), errors);
@ -52,7 +52,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_number_is_less_than_min() public async Task Should_add_errors_if_number_is_less_than_min()
{ {
var sut = new NumberField(1, "my-number", Partitioning.Invariant, new NumberFieldProperties { MinValue = 10 }); var sut = Field(new NumberFieldProperties { MinValue = 10 });
await sut.ValidateAsync(CreateValue(5), errors); await sut.ValidateAsync(CreateValue(5), errors);
@ -63,7 +63,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_number_is_greater_than_max() public async Task Should_add_errors_if_number_is_greater_than_max()
{ {
var sut = new NumberField(1, "my-number", Partitioning.Invariant, new NumberFieldProperties { MaxValue = 10 }); var sut = Field(new NumberFieldProperties { MaxValue = 10 });
await sut.ValidateAsync(CreateValue(20), errors); await sut.ValidateAsync(CreateValue(20), errors);
@ -74,7 +74,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_number_is_not_allowed() public async Task Should_add_errors_if_number_is_not_allowed()
{ {
var sut = new NumberField(1, "my-number", Partitioning.Invariant, new NumberFieldProperties { AllowedValues = ImmutableList.Create(10d) }); var sut = Field(new NumberFieldProperties { AllowedValues = ImmutableList.Create(10d) });
await sut.ValidateAsync(CreateValue(20), errors); await sut.ValidateAsync(CreateValue(20), errors);
@ -85,7 +85,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_value_is_not_valid() public async Task Should_add_errors_if_value_is_not_valid()
{ {
var sut = new NumberField(1, "my-number", Partitioning.Invariant); var sut = Field(new NumberFieldProperties());
await sut.ValidateAsync(CreateValue("Invalid"), errors); await sut.ValidateAsync(CreateValue("Invalid"), errors);
@ -97,5 +97,10 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
{ {
return new JValue(v); return new JValue(v);
} }
private static NumberField Field(NumberFieldProperties properties)
{
return new NumberField(1, "my-number", Partitioning.Invariant, properties);
}
} }
} }

25
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs

@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public void Should_instantiate_field() public void Should_instantiate_field()
{ {
var sut = new ReferencesField(1, "my-refs", Partitioning.Invariant); var sut = Field(new ReferencesFieldProperties());
Assert.Equal("my-refs", sut.Name); Assert.Equal("my-refs", sut.Name);
} }
@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
{ {
var referenceId = Guid.NewGuid(); var referenceId = Guid.NewGuid();
var sut = new ReferencesField(1, "my-refs", Partitioning.Invariant); var sut = Field(new ReferencesFieldProperties());
await sut.ValidateAsync(CreateValue(referenceId), errors, ValidationTestExtensions.ValidContext); await sut.ValidateAsync(CreateValue(referenceId), errors, ValidationTestExtensions.ValidContext);
@ -45,7 +45,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_not_add_error_if_references_are_null_and_valid() public async Task Should_not_add_error_if_references_are_null_and_valid()
{ {
var sut = new ReferencesField(1, "my-refs", Partitioning.Invariant); var sut = Field(new ReferencesFieldProperties());
await sut.ValidateAsync(CreateValue(null), errors); await sut.ValidateAsync(CreateValue(null), errors);
@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_references_are_required_and_null() public async Task Should_add_errors_if_references_are_required_and_null()
{ {
var sut = new ReferencesField(1, "my-refs", Partitioning.Invariant, new ReferencesFieldProperties { SchemaId = schemaId, IsRequired = true }); var sut = Field(new ReferencesFieldProperties { SchemaId = schemaId, IsRequired = true });
await sut.ValidateAsync(CreateValue(null), errors); await sut.ValidateAsync(CreateValue(null), errors);
@ -66,7 +66,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_references_are_required_and_empty() public async Task Should_add_errors_if_references_are_required_and_empty()
{ {
var sut = new ReferencesField(1, "my-refs", Partitioning.Invariant, new ReferencesFieldProperties { SchemaId = schemaId, IsRequired = true }); var sut = Field(new ReferencesFieldProperties { SchemaId = schemaId, IsRequired = true });
await sut.ValidateAsync(CreateValue(), errors); await sut.ValidateAsync(CreateValue(), errors);
@ -77,7 +77,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_value_is_not_valid() public async Task Should_add_errors_if_value_is_not_valid()
{ {
var sut = new ReferencesField(1, "my-refs", Partitioning.Invariant); var sut = Field(new ReferencesFieldProperties());
await sut.ValidateAsync("invalid", errors); await sut.ValidateAsync("invalid", errors);
@ -88,7 +88,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_value_has_not_enough_items() public async Task Should_add_errors_if_value_has_not_enough_items()
{ {
var sut = new ReferencesField(1, "my-refs", Partitioning.Invariant, new ReferencesFieldProperties { SchemaId = schemaId, MinItems = 3 }); var sut = Field(new ReferencesFieldProperties { SchemaId = schemaId, MinItems = 3 });
await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors); await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors);
@ -99,7 +99,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_value_has_too_much_items() public async Task Should_add_errors_if_value_has_too_much_items()
{ {
var sut = new ReferencesField(1, "my-refs", Partitioning.Invariant, new ReferencesFieldProperties { SchemaId = schemaId, MaxItems = 1 }); var sut = Field(new ReferencesFieldProperties { SchemaId = schemaId, MaxItems = 1 });
await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors); await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors);
@ -112,9 +112,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
{ {
var referenceId = Guid.NewGuid(); var referenceId = Guid.NewGuid();
var sut = new ReferencesField(1, "my-refs", Partitioning.Invariant, new ReferencesFieldProperties { SchemaId = schemaId }); var sut = Field(new ReferencesFieldProperties { SchemaId = schemaId });
await sut.ValidateAsync(CreateValue(referenceId), errors, ValidationTestExtensions.InvalidContext(referenceId)); await sut.ValidateAsync(CreateValue(referenceId), errors, ValidationTestExtensions.InvalidReferences(referenceId));
errors.ShouldBeEquivalentTo( errors.ShouldBeEquivalentTo(
new[] { $"<FIELD> contains invalid reference '{referenceId}'." }); new[] { $"<FIELD> contains invalid reference '{referenceId}'." });
@ -124,5 +124,10 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
{ {
return ids == null ? JValue.CreateNull() : (JToken)new JArray(ids.OfType<object>().ToArray()); return ids == null ? JValue.CreateNull() : (JToken)new JArray(ids.OfType<object>().ToArray());
} }
private static ReferencesField Field(ReferencesFieldProperties properties)
{
return new ReferencesField(1, "my-refs", Partitioning.Invariant, properties);
}
} }
} }

21
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/StringFieldTests.cs

@ -23,7 +23,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public void Should_instantiate_field() public void Should_instantiate_field()
{ {
var sut = new StringField(1, "my-string", Partitioning.Invariant); var sut = Field(new StringFieldProperties());
Assert.Equal("my-string", sut.Name); Assert.Equal("my-string", sut.Name);
} }
@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_not_add_error_if_string_is_valid() public async Task Should_not_add_error_if_string_is_valid()
{ {
var sut = new StringField(1, "my-string", Partitioning.Invariant, new StringFieldProperties { Label = "<FIELD>" }); var sut = Field(new StringFieldProperties { Label = "<FIELD>" });
await sut.ValidateAsync(CreateValue(null), errors); await sut.ValidateAsync(CreateValue(null), errors);
@ -41,7 +41,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_string_is_required() public async Task Should_add_errors_if_string_is_required()
{ {
var sut = new StringField(1, "my-string", Partitioning.Invariant, new StringFieldProperties { IsRequired = true }); var sut = Field(new StringFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(null), errors); await sut.ValidateAsync(CreateValue(null), errors);
@ -52,7 +52,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_string_is_shorter_than_min_length() public async Task Should_add_errors_if_string_is_shorter_than_min_length()
{ {
var sut = new StringField(1, "my-string", Partitioning.Invariant, new StringFieldProperties { MinLength = 10 }); var sut = Field(new StringFieldProperties { MinLength = 10 });
await sut.ValidateAsync(CreateValue("123"), errors); await sut.ValidateAsync(CreateValue("123"), errors);
@ -63,7 +63,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_string_is_longer_than_max_length() public async Task Should_add_errors_if_string_is_longer_than_max_length()
{ {
var sut = new StringField(1, "my-string", Partitioning.Invariant, new StringFieldProperties { MaxLength = 5 }); var sut = Field(new StringFieldProperties { MaxLength = 5 });
await sut.ValidateAsync(CreateValue("12345678"), errors); await sut.ValidateAsync(CreateValue("12345678"), errors);
@ -74,7 +74,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_string_not_allowed() public async Task Should_add_errors_if_string_not_allowed()
{ {
var sut = new StringField(1, "my-string", Partitioning.Invariant, new StringFieldProperties { AllowedValues = ImmutableList.Create("Foo") }); var sut = Field(new StringFieldProperties { AllowedValues = ImmutableList.Create("Foo") });
await sut.ValidateAsync(CreateValue("Bar"), errors); await sut.ValidateAsync(CreateValue("Bar"), errors);
@ -85,7 +85,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_number_is_not_valid_pattern() public async Task Should_add_errors_if_number_is_not_valid_pattern()
{ {
var sut = new StringField(1, "my-string", Partitioning.Invariant, new StringFieldProperties { Pattern = "[0-9]{3}" }); var sut = Field(new StringFieldProperties { Pattern = "[0-9]{3}" });
await sut.ValidateAsync(CreateValue("abc"), errors); await sut.ValidateAsync(CreateValue("abc"), errors);
@ -96,7 +96,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_number_is_not_valid_pattern_with_message() public async Task Should_add_errors_if_number_is_not_valid_pattern_with_message()
{ {
var sut = new StringField(1, "my-string", Partitioning.Invariant, new StringFieldProperties { Pattern = "[0-9]{3}", PatternMessage = "Custom Error Message." }); var sut = Field(new StringFieldProperties { Pattern = "[0-9]{3}", PatternMessage = "Custom Error Message." });
await sut.ValidateAsync(CreateValue("abc"), errors); await sut.ValidateAsync(CreateValue("abc"), errors);
@ -108,5 +108,10 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
{ {
return new JValue(v); return new JValue(v);
} }
private static StringField Field(StringFieldProperties properties)
{
return new StringField(1, "my-string", Partitioning.Invariant, properties);
}
} }
} }

21
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs

@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public void Should_instantiate_field() public void Should_instantiate_field()
{ {
var sut = new TagsField(1, "my-tags", Partitioning.Invariant); var sut = Field(new TagsFieldProperties());
Assert.Equal("my-tags", sut.Name); Assert.Equal("my-tags", sut.Name);
} }
@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
{ {
var referenceId = Guid.NewGuid(); var referenceId = Guid.NewGuid();
var sut = new TagsField(1, "my-tags", Partitioning.Invariant); var sut = Field(new TagsFieldProperties());
await sut.ValidateAsync(CreateValue(referenceId), errors, ValidationTestExtensions.ValidContext); await sut.ValidateAsync(CreateValue(referenceId), errors, ValidationTestExtensions.ValidContext);
@ -44,7 +44,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_not_add_error_if_tags_are_null_and_valid() public async Task Should_not_add_error_if_tags_are_null_and_valid()
{ {
var sut = new TagsField(1, "my-tags", Partitioning.Invariant); var sut = Field(new TagsFieldProperties());
await sut.ValidateAsync(CreateValue(null), errors); await sut.ValidateAsync(CreateValue(null), errors);
@ -54,7 +54,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_tags_are_required_and_null() public async Task Should_add_errors_if_tags_are_required_and_null()
{ {
var sut = new TagsField(1, "my-tags", Partitioning.Invariant, new TagsFieldProperties { IsRequired = true }); var sut = Field(new TagsFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(null), errors); await sut.ValidateAsync(CreateValue(null), errors);
@ -65,7 +65,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_tags_are_required_and_empty() public async Task Should_add_errors_if_tags_are_required_and_empty()
{ {
var sut = new TagsField(1, "my-tags", Partitioning.Invariant, new TagsFieldProperties { IsRequired = true }); var sut = Field(new TagsFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(), errors); await sut.ValidateAsync(CreateValue(), errors);
@ -76,7 +76,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_value_is_not_valid() public async Task Should_add_errors_if_value_is_not_valid()
{ {
var sut = new TagsField(1, "my-tags", Partitioning.Invariant); var sut = Field(new TagsFieldProperties());
await sut.ValidateAsync("invalid", errors); await sut.ValidateAsync("invalid", errors);
@ -87,7 +87,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_value_has_not_enough_items() public async Task Should_add_errors_if_value_has_not_enough_items()
{ {
var sut = new TagsField(1, "my-tags", Partitioning.Invariant, new TagsFieldProperties { MinItems = 3 }); var sut = Field(new TagsFieldProperties { MinItems = 3 });
await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors); await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors);
@ -98,7 +98,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_value_has_too_much_items() public async Task Should_add_errors_if_value_has_too_much_items()
{ {
var sut = new TagsField(1, "my-tags", Partitioning.Invariant, new TagsFieldProperties { MaxItems = 1 }); var sut = Field(new TagsFieldProperties { MaxItems = 1 });
await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors); await sut.ValidateAsync(CreateValue(Guid.NewGuid(), Guid.NewGuid()), errors);
@ -110,5 +110,10 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
{ {
return ids == null ? JValue.CreateNull() : (JToken)new JArray(ids.OfType<object>().ToArray()); return ids == null ? JValue.CreateNull() : (JToken)new JArray(ids.OfType<object>().ToArray());
} }
private static TagsField Field(TagsFieldProperties properties)
{
return new TagsField(1, "my-tags", Partitioning.Invariant, properties);
}
} }
} }

19
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs

@ -8,6 +8,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
@ -18,9 +19,10 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
{ {
public static class ValidationTestExtensions public static class ValidationTestExtensions
{ {
private static readonly Task<IReadOnlyList<Guid>> ValidIds = Task.FromResult<IReadOnlyList<Guid>>(new List<Guid>()); private static readonly Task<IReadOnlyList<Guid>> ValidReferences = Task.FromResult<IReadOnlyList<Guid>>(new List<Guid>());
private static readonly Task<IReadOnlyList<IAssetInfo>> ValidAssets = Task.FromResult<IReadOnlyList<IAssetInfo>>(new List<IAssetInfo>());
public static readonly ValidationContext ValidContext = new ValidationContext((x, y) => ValidIds, x => ValidIds); public static readonly ValidationContext ValidContext = new ValidationContext((x, y) => ValidReferences, x => ValidAssets);
public static Task ValidateAsync(this IValidator validator, object value, IList<string> errors, ValidationContext context = null) public static Task ValidateAsync(this IValidator validator, object value, IList<string> errors, ValidationContext context = null)
{ {
@ -42,11 +44,18 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
return field.ValidateAsync(value, (context ?? ValidContext).Optional(true), errors.Add); return field.ValidateAsync(value, (context ?? ValidContext).Optional(true), errors.Add);
} }
public static ValidationContext InvalidContext(Guid assetId) public static ValidationContext Assets(params IAssetInfo[] assets)
{ {
var invalidIds = Task.FromResult<IReadOnlyList<Guid>>(new List<Guid> { assetId }); var actual = Task.FromResult<IReadOnlyList<IAssetInfo>>(assets.ToList());
return new ValidationContext((x, y) => invalidIds, x => invalidIds); return new ValidationContext((x, y) => ValidReferences, x => actual);
}
public static ValidationContext InvalidReferences(Guid referencesIds)
{
var actual = Task.FromResult<IReadOnlyList<Guid>>(new List<Guid> { referencesIds });
return new ValidationContext((x, y) => actual, x => ValidAssets);
} }
} }
} }

2
tests/Squidex.Domain.Apps.Read.Tests/Contents/TestData/FakeAssetEntity.cs

@ -19,6 +19,8 @@ namespace Squidex.Domain.Apps.Read.Contents.TestData
public Guid AppId { get; set; } public Guid AppId { get; set; }
public Guid AssetId { get; set; }
public Instant Created { get; set; } public Instant Created { get; set; }
public Instant LastModified { get; set; } public Instant LastModified { get; set; }

72
tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/FieldProperties/AssetsFieldPropertiesTests.cs

@ -18,7 +18,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards.FieldProperties
public class AssetsFieldPropertiesTests public class AssetsFieldPropertiesTests
{ {
[Fact] [Fact]
public void Should_add_error_if_min_greater_than_max() public void Should_add_error_if_min_items_greater_than_max_items()
{ {
var sut = new AssetsFieldProperties { MinItems = 10, MaxItems = 5 }; var sut = new AssetsFieldProperties { MinItems = 10, MaxItems = 5 };
@ -30,5 +30,75 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards.FieldProperties
new ValidationError("Max items must be greater than min items.", "MinItems", "MaxItems") new ValidationError("Max items must be greater than min items.", "MinItems", "MaxItems")
}); });
} }
[Fact]
public void Should_add_error_if_min_width_greater_than_max_width()
{
var sut = new AssetsFieldProperties { MinWidth = 10, MaxWidth = 5 };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
errors.ShouldBeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Max width must be greater than min width.", "MinWidth", "MaxWidth")
});
}
[Fact]
public void Should_add_error_if_min_height_greater_than_max_height()
{
var sut = new AssetsFieldProperties { MinHeight = 10, MaxHeight = 5 };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
errors.ShouldBeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Max height must be greater than min height.", "MinHeight", "MaxHeight")
});
}
[Fact]
public void Should_add_error_if_min_size_greater_than_max_size()
{
var sut = new AssetsFieldProperties { MinSize = 10, MaxSize = 5 };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
errors.ShouldBeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Max size must be greater than min size.", "MinSize", "MaxSize")
});
}
[Fact]
public void Should_add_error_if_only_aspect_width_is_defined()
{
var sut = new AssetsFieldProperties { AspectWidth = 10 };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
errors.ShouldBeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Aspect width and height must be defined.", "AspectWidth", "AspectHeight")
});
}
[Fact]
public void Should_add_error_if_only_aspect_height_is_defined()
{
var sut = new AssetsFieldProperties { AspectHeight = 10 };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
errors.ShouldBeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Aspect width and height must be defined.", "AspectWidth", "AspectHeight")
});
}
} }
} }

Loading…
Cancel
Save