From 247bfe3f36002f51223f4556fd8feddefc78f5f8 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Tue, 11 Apr 2017 19:16:19 +0200 Subject: [PATCH] New API structure --- .../Assets/AssetFile.cs | 44 ++++++ .../Assets/FolderAssetStore.cs | 20 +-- .../Assets/IAssetStore.cs | 5 +- .../Assets/ImageInfo.cs | 5 +- .../Assets/MongoAssetRepository.cs | 5 + src/Squidex.Write/Apps/AppDomainObject.cs | 2 +- src/Squidex.Write/Apps/Commands/CreateApp.cs | 9 +- .../Assets/AssetCommandHandler.cs | 33 ++++- src/Squidex.Write/Assets/AssetDomainObject.cs | 29 +++- .../Assets/Commands/CreateAsset.cs | 14 +- .../Assets/Commands/UpdateAsset.cs | 12 +- .../Api/Assets/AssetContentController.cs | 27 +--- .../Controllers/Api/Assets/AssetController.cs | 131 ++++++++++++------ .../Controllers/Api/Assets/Models/AssetDto.cs | 12 +- .../Api/Assets/Models/AssetUpdateDto.cs | 21 +++ src/Squidex/Squidex.csproj | 1 + .../assets/pages/asset.component.html | 14 +- .../assets/pages/asset.component.scss | 41 +++++- .../features/assets/pages/asset.component.ts | 51 +++++++ .../app/shared/services/assets.service.ts | 2 +- src/Squidex/wwwroot/images/asset_doc.png | Bin 0 -> 911 bytes src/Squidex/wwwroot/images/asset_docx.png | Bin 0 -> 958 bytes src/Squidex/wwwroot/images/asset_generic.png | Bin 0 -> 514 bytes src/Squidex/wwwroot/images/asset_pdf.png | Bin 0 -> 1156 bytes src/Squidex/wwwroot/images/asset_ppt.png | Bin 0 -> 1034 bytes src/Squidex/wwwroot/images/asset_pptx.png | Bin 0 -> 1058 bytes src/Squidex/wwwroot/images/asset_video.png | Bin 0 -> 723 bytes src/Squidex/wwwroot/images/asset_xls.png | Bin 0 -> 862 bytes src/Squidex/wwwroot/images/asset_xlsx.png | Bin 0 -> 905 bytes .../Apps/AppCommandHandlerTests.cs | 6 +- .../Apps/AppDomainObjectTests.cs | 2 +- .../Assets/AssetCommandHandlerTests.cs | 36 +++-- .../Assets/AssetDomainObjectTests.cs | 42 ++++-- .../Contents/ContentCommandHandlerTests.cs | 2 +- .../Schemas/SchemaCommandHandlerTests.cs | 2 +- 35 files changed, 424 insertions(+), 144 deletions(-) create mode 100644 src/Squidex.Infrastructure/Assets/AssetFile.cs create mode 100644 src/Squidex/Controllers/Api/Assets/Models/AssetUpdateDto.cs create mode 100644 src/Squidex/wwwroot/images/asset_doc.png create mode 100644 src/Squidex/wwwroot/images/asset_docx.png create mode 100644 src/Squidex/wwwroot/images/asset_generic.png create mode 100644 src/Squidex/wwwroot/images/asset_pdf.png create mode 100644 src/Squidex/wwwroot/images/asset_ppt.png create mode 100644 src/Squidex/wwwroot/images/asset_pptx.png create mode 100644 src/Squidex/wwwroot/images/asset_video.png create mode 100644 src/Squidex/wwwroot/images/asset_xls.png create mode 100644 src/Squidex/wwwroot/images/asset_xlsx.png diff --git a/src/Squidex.Infrastructure/Assets/AssetFile.cs b/src/Squidex.Infrastructure/Assets/AssetFile.cs new file mode 100644 index 000000000..1c48a4f3d --- /dev/null +++ b/src/Squidex.Infrastructure/Assets/AssetFile.cs @@ -0,0 +1,44 @@ +// ========================================================================== +// IAssetFile.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.IO; + +namespace Squidex.Infrastructure.Assets +{ + public sealed class AssetFile + { + private readonly Func openAction; + + public string FileName { get; } + + public string MimeType { get; } + + public long FileSize { get; } + + public AssetFile(string fileName, string mimeType, long fileSize, Func openAction) + { + Guard.NotNullOrEmpty(fileName, nameof(fileName)); + Guard.NotNullOrEmpty(mimeType, nameof(mimeType)); + Guard.NotNull(openAction, nameof(openAction)); + Guard.GreaterThan(fileSize, 0, nameof(fileSize)); + + FileName = fileName; + FileSize = fileSize; + + MimeType = mimeType; + + this.openAction = openAction; + } + + public Stream OpenRead() + { + return openAction(); + } + } +} diff --git a/src/Squidex.Infrastructure/Assets/FolderAssetStore.cs b/src/Squidex.Infrastructure/Assets/FolderAssetStore.cs index 1916cfc37..90e644c04 100644 --- a/src/Squidex.Infrastructure/Assets/FolderAssetStore.cs +++ b/src/Squidex.Infrastructure/Assets/FolderAssetStore.cs @@ -6,6 +6,7 @@ // All rights reserved. // ========================================================================== +using System; using System.IO; using System.Threading.Tasks; using Squidex.Infrastructure.Log; @@ -49,9 +50,9 @@ namespace Squidex.Infrastructure.Assets } } - public Task GetAssetAsync(string name) + public Task GetAssetAsync(Guid id, long version, string suffix = null) { - var file = GetFile(name); + var file = GetFile(id, version, suffix); Stream stream = null; @@ -70,9 +71,9 @@ namespace Squidex.Infrastructure.Assets return Task.FromResult(stream); } - public async Task UploadAssetAsync(string name, Stream stream) + public async Task UploadAssetAsync(Guid id, long version, Stream stream, string suffix = null) { - var file = GetFile(name); + var file = GetFile(id, version, suffix); using (var fileStream = file.OpenWrite()) { @@ -80,13 +81,16 @@ namespace Squidex.Infrastructure.Assets } } - private FileInfo GetFile(string name) + private FileInfo GetFile(Guid id, long version, string suffix) { - Guard.ValidFileName(name, nameof(name)); + var path = Path.Combine(directory.FullName, $"{id}_{version}"); - var file = new FileInfo(Path.Combine(directory.FullName, name)); + if (!string.IsNullOrWhiteSpace(suffix)) + { + path += "_" + suffix; + } - return file; + return new FileInfo(path); } } } diff --git a/src/Squidex.Infrastructure/Assets/IAssetStore.cs b/src/Squidex.Infrastructure/Assets/IAssetStore.cs index bf296eacc..e0926ad01 100644 --- a/src/Squidex.Infrastructure/Assets/IAssetStore.cs +++ b/src/Squidex.Infrastructure/Assets/IAssetStore.cs @@ -6,6 +6,7 @@ // All rights reserved. // ========================================================================== +using System; using System.IO; using System.Threading.Tasks; @@ -13,8 +14,8 @@ namespace Squidex.Infrastructure.Assets { public interface IAssetStore { - Task GetAssetAsync(string name); + Task GetAssetAsync(Guid id, long version, string suffix = null); - Task UploadAssetAsync(string name, Stream stream); + Task UploadAssetAsync(Guid id, long version, Stream stream, string suffix = null); } } \ No newline at end of file diff --git a/src/Squidex.Infrastructure/Assets/ImageInfo.cs b/src/Squidex.Infrastructure/Assets/ImageInfo.cs index 36bd06b53..6c30fb2d7 100644 --- a/src/Squidex.Infrastructure/Assets/ImageInfo.cs +++ b/src/Squidex.Infrastructure/Assets/ImageInfo.cs @@ -3,7 +3,7 @@ // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group -// All rights reserved. +// All rights reserved.d // ========================================================================== namespace Squidex.Infrastructure.Assets @@ -16,6 +16,9 @@ namespace Squidex.Infrastructure.Assets public ImageInfo(int pixelWidth, int pixelHeight) { + Guard.GreaterThan(pixelWidth, 0, nameof(pixelWidth)); + Guard.GreaterThan(pixelHeight, 0, nameof(pixelHeight)); + PixelWidth = pixelWidth; PixelHeight = pixelHeight; } diff --git a/src/Squidex.Read.MongoDb/Assets/MongoAssetRepository.cs b/src/Squidex.Read.MongoDb/Assets/MongoAssetRepository.cs index 9be371dbe..1d7ddc2bd 100644 --- a/src/Squidex.Read.MongoDb/Assets/MongoAssetRepository.cs +++ b/src/Squidex.Read.MongoDb/Assets/MongoAssetRepository.cs @@ -31,6 +31,11 @@ namespace Squidex.Read.MongoDb.Assets return "Projections_Assets"; } + protected override Task SetupCollectionAsync(IMongoCollection collection) + { + return Collection.Indexes.CreateOneAsync(IndexKeys.Descending(x => x.LastModified).Ascending(x => x.AppId).Ascending(x => x.FileName).Ascending(x => x.MimeType)); + } + public async Task> QueryAsync(Guid appId, HashSet mimeTypes = null, string query = null, int take = 10, int skip = 0) { var filter = CreateFilter(appId, mimeTypes, query); diff --git a/src/Squidex.Write/Apps/AppDomainObject.cs b/src/Squidex.Write/Apps/AppDomainObject.cs index 52dc6ea60..35fbbdbe0 100644 --- a/src/Squidex.Write/Apps/AppDomainObject.cs +++ b/src/Squidex.Write/Apps/AppDomainObject.cs @@ -101,7 +101,7 @@ namespace Squidex.Write.Apps ThrowIfCreated(); - var appId = new NamedId(command.AggregateId, command.Name); + var appId = new NamedId(command.AppId, command.Name); RaiseEvent(SimpleMapper.Map(command, new AppCreated { AppId = appId })); diff --git a/src/Squidex.Write/Apps/Commands/CreateApp.cs b/src/Squidex.Write/Apps/Commands/CreateApp.cs index ae3bb8a5d..7d8e4dfc9 100644 --- a/src/Squidex.Write/Apps/Commands/CreateApp.cs +++ b/src/Squidex.Write/Apps/Commands/CreateApp.cs @@ -17,11 +17,16 @@ namespace Squidex.Write.Apps.Commands { public string Name { get; set; } - public Guid AggregateId { get; set; } + public Guid AppId { get; set; } + + Guid IAggregateCommand.AggregateId + { + get { return AppId; } + } public CreateApp() { - AggregateId = Guid.NewGuid(); + AppId = Guid.NewGuid(); } public void Validate(IList errors) diff --git a/src/Squidex.Write/Assets/AssetCommandHandler.cs b/src/Squidex.Write/Assets/AssetCommandHandler.cs index 60d1aa496..90d5876ff 100644 --- a/src/Squidex.Write/Assets/AssetCommandHandler.cs +++ b/src/Squidex.Write/Assets/AssetCommandHandler.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Squidex.Infrastructure; +using Squidex.Infrastructure.Assets; using Squidex.Infrastructure.CQRS.Commands; using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.Tasks; @@ -18,32 +19,52 @@ namespace Squidex.Write.Assets public class AssetCommandHandler : ICommandHandler { private readonly IAggregateHandler handler; + private readonly IAssetStore assetStore; + private readonly IAssetThumbnailGenerator assetThumbnailGenerator; - public AssetCommandHandler(IAggregateHandler handler) + public AssetCommandHandler( + IAggregateHandler handler, + IAssetStore assetStore, + IAssetThumbnailGenerator assetThumbnailGenerator) { Guard.NotNull(handler, nameof(handler)); + Guard.NotNull(assetStore, nameof(assetStore)); + Guard.NotNull(assetThumbnailGenerator, nameof(assetThumbnailGenerator)); this.handler = handler; + this.assetStore = assetStore; + this.assetThumbnailGenerator = assetThumbnailGenerator; } protected async Task On(CreateAsset command, CommandContext context) { - await handler.CreateAsync(context, c => + await handler.CreateAsync(context, async c => { + command.ImageInfo = await assetThumbnailGenerator.GetImageInfoAsync(command.File.OpenRead()); + c.Create(command); + await assetStore.UploadAssetAsync(c.Id, c.Version, command.File.OpenRead()); + context.Succeed(EntityCreatedResult.Create(c.Id, c.Version)); }); } - protected async Task On(RenameAsset command, CommandContext context) + protected async Task On(UpdateAsset command, CommandContext context) { - await handler.UpdateAsync(context, c => c.Rename(command)); + await handler.UpdateAsync(context, async c => + { + command.ImageInfo = await assetThumbnailGenerator.GetImageInfoAsync(command.File.OpenRead()); + + c.Update(command); + + await assetStore.UploadAssetAsync(c.Id, c.Version, command.File.OpenRead()); + }); } - protected async Task On(UpdateAsset command, CommandContext context) + protected Task On(RenameAsset command, CommandContext context) { - await handler.UpdateAsync(context, c => c.Update(command)); + return handler.UpdateAsync(context, c => c.Rename(command)); } protected Task On(DeleteAsset command, CommandContext context) diff --git a/src/Squidex.Write/Assets/AssetDomainObject.cs b/src/Squidex.Write/Assets/AssetDomainObject.cs index eca149e2f..aff28018f 100644 --- a/src/Squidex.Write/Assets/AssetDomainObject.cs +++ b/src/Squidex.Write/Assets/AssetDomainObject.cs @@ -60,29 +60,48 @@ namespace Squidex.Write.Assets VerifyNotCreated(); - RaiseEvent(SimpleMapper.Map(command, new AssetCreated())); + var @event = SimpleMapper.Map(command, new AssetCreated + { + FileName = command.File.FileName, + FileSize = command.File.FileSize, + MimeType = command.File.MimeType, + PixelWidth = command.ImageInfo?.PixelWidth, + PixelHeight = command.ImageInfo?.PixelHeight, + IsImage = command.ImageInfo != null + }); + + RaiseEvent(@event); return this; } - public AssetDomainObject Delete(DeleteAsset command) + public AssetDomainObject Update(UpdateAsset command) { Guard.NotNull(command, nameof(command)); VerifyCreatedAndNotDeleted(); - RaiseEvent(SimpleMapper.Map(command, new AssetDeleted())); + var @event = SimpleMapper.Map(command, new AssetUpdated + { + FileSize = command.File.FileSize, + MimeType = command.File.MimeType, + PixelWidth = command.ImageInfo?.PixelWidth, + PixelHeight = command.ImageInfo?.PixelHeight, + IsImage = command.ImageInfo != null + }); + + RaiseEvent(@event); return this; } - public AssetDomainObject Update(UpdateAsset command) + public AssetDomainObject Delete(DeleteAsset command) { Guard.NotNull(command, nameof(command)); VerifyCreatedAndNotDeleted(); - RaiseEvent(SimpleMapper.Map(command, new AssetUpdated())); + RaiseEvent(SimpleMapper.Map(command, new AssetDeleted())); return this; } diff --git a/src/Squidex.Write/Assets/Commands/CreateAsset.cs b/src/Squidex.Write/Assets/Commands/CreateAsset.cs index 501502b14..66f321237 100644 --- a/src/Squidex.Write/Assets/Commands/CreateAsset.cs +++ b/src/Squidex.Write/Assets/Commands/CreateAsset.cs @@ -6,20 +6,14 @@ // All rights reserved. // ========================================================================== +using Squidex.Infrastructure.Assets; + namespace Squidex.Write.Assets.Commands { public sealed class CreateAsset : AssetAggregateCommand { - public string FileName { get; set; } - - public string MimeType { get; set; } - - public long FileSize { get; set; } - - public bool IsImage { get; set; } - - public int? PixelWidth { get; set; } + public AssetFile File { get; set; } - public int? PixelHeight { get; set; } + public ImageInfo ImageInfo { get; set; } } } diff --git a/src/Squidex.Write/Assets/Commands/UpdateAsset.cs b/src/Squidex.Write/Assets/Commands/UpdateAsset.cs index 754b8ca0d..0c835e30a 100644 --- a/src/Squidex.Write/Assets/Commands/UpdateAsset.cs +++ b/src/Squidex.Write/Assets/Commands/UpdateAsset.cs @@ -6,18 +6,14 @@ // All rights reserved. // ========================================================================== +using Squidex.Infrastructure.Assets; + namespace Squidex.Write.Assets.Commands { public class UpdateAsset : AssetAggregateCommand { - public string MimeType { get; set; } - - public long FileSize { get; set; } - - public bool IsImage { get; set; } - - public int? PixelWidth { get; set; } + public AssetFile File { get; set; } - public int? PixelHeight { get; set; } + public ImageInfo ImageInfo { get; set; } } } diff --git a/src/Squidex/Controllers/Api/Assets/AssetContentController.cs b/src/Squidex/Controllers/Api/Assets/AssetContentController.cs index 59b90d435..a68716528 100644 --- a/src/Squidex/Controllers/Api/Assets/AssetContentController.cs +++ b/src/Squidex/Controllers/Api/Assets/AssetContentController.cs @@ -20,12 +20,9 @@ using Squidex.Read.Assets.Repositories; namespace Squidex.Controllers.Api.Assets { - /// - /// Uploads and retrieves assets. - /// [ApiExceptionFilter] [ServiceFilter(typeof(AppFilterAttribute))] - [SwaggerTag("Assets")] + [SwaggerIgnore] public class AssetContentController : ControllerBase { private readonly IAssetStore assetStorage; @@ -44,18 +41,6 @@ namespace Squidex.Controllers.Api.Assets this.assetThumbnailGenerator = assetThumbnailGenerator; } - /// - /// Gets the content of the asset. - /// - /// The name of the app. - /// The id of the asset. - /// The resize mode. - /// The target width of the image. - /// The target width of the image. - /// - /// 200 => Asset content returned. - /// 404 => App or Asset not found. - /// [HttpGet] [Route("assets/{id}/")] public async Task GetAssetContent(string app, Guid id, [FromQuery] int? width = null, [FromQuery] int? height = null, [FromQuery] string mode = null) @@ -71,13 +56,13 @@ namespace Squidex.Controllers.Api.Assets if (asset.IsImage && (width.HasValue || height.HasValue)) { - var name = $"{asset.Id}_{asset.Version}_{width}_{height}_{mode}"; + var suffix = $"{width}_{height}_{mode}"; - content = await assetStorage.GetAssetAsync(name); + content = await assetStorage.GetAssetAsync(asset.Id, asset.Version, suffix); if (content == null) { - var fullSizeContent = await assetStorage.GetAssetAsync($"{asset.Id}_{asset.Version}"); + var fullSizeContent = await assetStorage.GetAssetAsync(asset.Id, asset.Version); if (fullSizeContent == null) { @@ -86,14 +71,14 @@ namespace Squidex.Controllers.Api.Assets content = await assetThumbnailGenerator.CreateThumbnailAsync(fullSizeContent, width, height, mode); - await assetStorage.UploadAssetAsync(name, content); + await assetStorage.UploadAssetAsync(asset.Id, asset.Version, content, suffix); content.Position = 0; } } else { - content = await assetStorage.GetAssetAsync($"{asset.Id}_{asset.Version}"); + content = await assetStorage.GetAssetAsync(asset.Id, asset.Version); } if (content == null) diff --git a/src/Squidex/Controllers/Api/Assets/AssetController.cs b/src/Squidex/Controllers/Api/Assets/AssetController.cs index 71a4f4099..e69f62578 100644 --- a/src/Squidex/Controllers/Api/Assets/AssetController.cs +++ b/src/Squidex/Controllers/Api/Assets/AssetController.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; @@ -26,8 +25,6 @@ using Squidex.Pipeline; using Squidex.Read.Assets.Repositories; using Squidex.Write.Assets.Commands; -#pragma warning disable 1573 - namespace Squidex.Controllers.Api.Assets { /// @@ -39,28 +36,26 @@ namespace Squidex.Controllers.Api.Assets [SwaggerTag("Assets")] public class AssetController : ControllerBase { - private readonly IAssetStore assetStorage; private readonly IAssetRepository assetRepository; - private readonly IAssetThumbnailGenerator assetThumbnailGenerator; private readonly AssetConfig assetsConfig; public AssetController( ICommandBus commandBus, - IAssetStore assetStorage, IAssetRepository assetRepository, - IAssetThumbnailGenerator assetThumbnailGenerator, IOptions assetsConfig) : base(commandBus) { - this.assetStorage = assetStorage; this.assetsConfig = assetsConfig.Value; this.assetRepository = assetRepository; - this.assetThumbnailGenerator = assetThumbnailGenerator; } /// /// Get assets. /// + /// The number of assets to skip. + /// The number of assets to take. + /// The query to limit the files by name. + /// Comma separated list of mime types to get. /// /// 200 => assets returned. /// @@ -96,7 +91,8 @@ namespace Squidex.Controllers.Api.Assets /// /// Creates and uploads a new asset. /// - /// The name of the app. + /// The app where the asset is a part of. + /// The file to upload. /// /// 201 => Asset created. /// 404 => App not found. @@ -106,53 +102,104 @@ namespace Squidex.Controllers.Api.Assets [Route("apps/{app}/assets/")] [ProducesResponseType(typeof(AssetDto), 201)] [ProducesResponseType(typeof(ErrorDto), 400)] - public async Task PostAsset(string app, List files) + public async Task PostAsset(string app, List file) { - if (files.Count != 1) - { - var error = new ValidationError($"Can only upload one file, found ${files.Count}."); + var assetFile = GetAssetFile(file); - throw new ValidationException("Cannot create asset.", error); - } + var command = new CreateAsset { File = assetFile }; + var context = await CommandBus.PublishAsync(command); - var file = files[0]; + var result = context.Result>(); + var response = AssetDto.Create(command, result); - if (file.Length > assetsConfig.MaxSize) - { - var error = new ValidationError($"File size cannot be longer than ${assetsConfig.MaxSize}."); + return StatusCode(201, response); + } - throw new ValidationException("Cannot create asset.", error); - } + /// + /// Replaces the content of the asset with a newer version. + /// + /// The app where the asset is a part of. + /// The id of the asset. + /// The file to upload. + /// + /// 201 => Asset updated. + /// 404 => App or Asset not found. + /// 400 => Asset exceeds the maximum size. + /// + [HttpPut] + [Route("apps/{app}/assets/{id}/content")] + [ProducesResponseType(typeof(AssetDto), 201)] + [ProducesResponseType(typeof(ErrorDto), 400)] + public async Task PutAssetContent(string app, Guid id, List file) + { + var assetFile = GetAssetFile(file); + + await CommandBus.PublishAsync(new UpdateAsset { File = assetFile }); - var fileContent = new MemoryStream(); + return NoContent(); + } - await file.OpenReadStream().CopyToAsync(fileContent); + /// + /// Updates the asset. + /// + /// The app where the asset is a part of. + /// The id of the asset. + /// The asset object that needs to updated. + /// + /// 201 => Asset updated. + /// 404 => App or Asset not found. + /// + [HttpPost] + [Route("apps/{app}/assets/{id}/content")] + [ProducesResponseType(typeof(AssetDto), 201)] + [ProducesResponseType(typeof(ErrorDto), 400)] + public async Task PutAsset(string app, Guid id, [FromBody] AssetUpdateDto request) + { + var command = SimpleMapper.Map(request, new RenameAsset()); + + await CommandBus.PublishAsync(command); - fileContent.Position = 0; + return NoContent(); + } + + /// + /// Delete an asset. + /// + /// The app where the schema is a part of. + /// The id of the asset to delete. + /// + /// 204 => Asset has been deleted. + /// + [HttpDelete] + [Route("apps/{app}/schemas/{name}/")] + public async Task DeleteSchema(string app, Guid id) + { + await CommandBus.PublishAsync(new DeleteAsset { AssetId = id }); - var imageInfo = await assetThumbnailGenerator.GetImageInfoAsync(fileContent); + return NoContent(); + } - var command = new CreateAsset + private AssetFile GetAssetFile(IReadOnlyList file) + { + if (file.Count != 1) { - AssetId = Guid.NewGuid(), - FileSize = file.Length, - FileName = file.FileName, - MimeType = file.ContentType, - IsImage = imageInfo != null, - PixelWidth = imageInfo?.PixelWidth, - PixelHeight = imageInfo?.PixelHeight - }; - - fileContent.Position = 0; + var error = new ValidationError($"Can only upload one file, found ${file.Count}."); - await assetStorage.UploadAssetAsync($"{command.AssetId}_0", fileContent); + throw new ValidationException("Cannot create asset.", error); + } - var context = await CommandBus.PublishAsync(command); + var formFile = file[0]; - var result = context.Result>(); - var response = AssetDto.Create(command, result); + if (formFile.Length > assetsConfig.MaxSize) + { + var error = new ValidationError($"File size cannot be longer than ${assetsConfig.MaxSize}."); - return StatusCode(201, response); + throw new ValidationException("Cannot create asset.", error); + } + + var assetFile = new AssetFile(formFile.FileName, formFile.ContentType, formFile.Length, formFile.OpenReadStream); + + return assetFile; } } } diff --git a/src/Squidex/Controllers/Api/Assets/Models/AssetDto.cs b/src/Squidex/Controllers/Api/Assets/Models/AssetDto.cs index 5f64319bd..21d5dbf5e 100644 --- a/src/Squidex/Controllers/Api/Assets/Models/AssetDto.cs +++ b/src/Squidex/Controllers/Api/Assets/Models/AssetDto.cs @@ -93,12 +93,12 @@ namespace Squidex.Controllers.Api.Assets.Models CreatedBy = command.Actor, LastModified = now, LastModifiedBy = command.Actor, - FileName = command.FileName, - FileSize = command.FileSize, - MimeType = command.MimeType, - IsImage = command.IsImage, - PixelWidth = command.PixelWidth, - PixelHeight = command.PixelHeight + FileName = command.File.FileName, + FileSize = command.File.FileSize, + MimeType = command.File.MimeType, + IsImage = command.ImageInfo != null, + PixelWidth = command.ImageInfo?.PixelWidth, + PixelHeight = command.ImageInfo?.PixelHeight }; return response; diff --git a/src/Squidex/Controllers/Api/Assets/Models/AssetUpdateDto.cs b/src/Squidex/Controllers/Api/Assets/Models/AssetUpdateDto.cs new file mode 100644 index 000000000..d627c24c4 --- /dev/null +++ b/src/Squidex/Controllers/Api/Assets/Models/AssetUpdateDto.cs @@ -0,0 +1,21 @@ +// ========================================================================== +// AssetUpdateDto.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System.ComponentModel.DataAnnotations; + +namespace Squidex.Controllers.Api.Assets.Models +{ + public class AssetUpdateDto + { + /// + /// The new name of the asset. + /// + [Required] + public string Name { get; set; } + } +} diff --git a/src/Squidex/Squidex.csproj b/src/Squidex/Squidex.csproj index a517add11..4a36a74d5 100644 --- a/src/Squidex/Squidex.csproj +++ b/src/Squidex/Squidex.csproj @@ -64,6 +64,7 @@ + diff --git a/src/Squidex/app/features/assets/pages/asset.component.html b/src/Squidex/app/features/assets/pages/asset.component.html index 91669dc4b..56ac6f642 100644 --- a/src/Squidex/app/features/assets/pages/asset.component.html +++ b/src/Squidex/app/features/assets/pages/asset.component.html @@ -1,12 +1,15 @@
-
+
-
+
{{fileType}}
-
+
+
+ +
+
+
+ + Drop to update +
\ No newline at end of file diff --git a/src/Squidex/app/features/assets/pages/asset.component.scss b/src/Squidex/app/features/assets/pages/asset.component.scss index 54771de50..8671a25d4 100644 --- a/src/Squidex/app/features/assets/pages/asset.component.scss +++ b/src/Squidex/app/features/assets/pages/asset.component.scss @@ -10,11 +10,42 @@ $color-type-foreground: #fff; padding-bottom: 1rem; } +.drop-overlay { + & { + @include transition(opacity.4s ease); + @include absolute(0, 0, 0, 0); + @include opacity(0); + pointer-events: none; + } + + &-text { + @include absolute(0, 0, 0, 0); + text-align: center; + line-height: 14rem; + font-size: 1.2rem; + font-weight: lighter; + color: $color-type-foreground; + } + + &-background { + @include absolute(0, 0, 0, 0); + @include opacity(.7); + background: $color-type-background; + } +} + .card { & { + position: relative; height: $card-size; } + &.drag { + .drop-overlay { + @include opacity(1); + } + } + &-block { padding: .8rem .8rem 0; position: relative; @@ -33,6 +64,13 @@ $color-type-foreground: #fff; font-size: .8rem; } + &-icon { + &-container { + background: $color-border; + height: 155px; + } + } + &-name { @include truncate; font-size: 1rem; @@ -47,7 +85,8 @@ $color-type-foreground: #fff; background: $color-type-background; border: 0; padding: .1rem .3rem; - font-size: .8rem; + text-transform: uppercase; + font-size: .7rem; font-weight: normal; color: $color-type-foreground; } diff --git a/src/Squidex/app/features/assets/pages/asset.component.ts b/src/Squidex/app/features/assets/pages/asset.component.ts index 31c419099..714670290 100644 --- a/src/Squidex/app/features/assets/pages/asset.component.ts +++ b/src/Squidex/app/features/assets/pages/asset.component.ts @@ -48,6 +48,16 @@ export class AssetComponent extends AppComponentBase implements OnInit { return result; } + public get fileIcon(): string { + let result = ''; + + if (this.asset != null) { + result = fileIcon(this.fileType); + } + + return result; + } + public get fileName(): string { let result = ''; @@ -108,4 +118,45 @@ function fileSize(b: number) { } return (u ? b.toFixed(1) + ' ' : b) + ' kMGTPEZY'[u] + 'B'; +} + +const mimeMapping = { + 'pdf': 'pdf', + 'vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx', + 'vnd.openxmlformats-officedocument.wordprocessingml.template': 'docx', + 'vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx', + 'vnd.openxmlformats-officedocument.spreadsheetml.template': 'xlsx', + 'vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx', + 'vnd.openxmlformats-officedocument.presentationml.template': 'pptx', + 'vnd.openxmlformats-officedocument.presentationml.slideshow': 'pptx', + 'msword': 'doc', + 'vnd.ms-word': 'doc', + 'vnd.ms-word.document.macroEnabled.12': 'docx', + 'vnd.ms-word.template.macroEnabled.12': 'docx', + 'vnd.ms-excel': 'xls', + 'vnd.ms-excel.sheet.macroEnabled.12': 'xlsx', + 'vnd.ms-excel.template.macroEnabled.12': 'xlsx', + 'vnd.ms-excel.addin.macroEnabled.12': 'xlsx', + 'vnd.ms-excel.sheet.binary.macroEnabled.12': 'xlsx', + 'vnd.ms-powerpoint': 'ppt', + 'vnd.ms-powerpoint.addin.macroEnabled.12': 'pptx', + 'vnd.ms-powerpoint.presentation.macroEnabled.12': 'pptx', + 'vnd.ms-powerpoint.template.macroEnabled.12': 'pptx', + 'vnd.ms-powerpoint.slideshow.macroEnabled.12': 'pptx' +}; + +function fileIcon(mimeType: string) { + const mimeParts = mimeType.split('/'); + const mimePrefix = mimeParts[0].toLowerCase(); + const mimeSuffix = mimeParts[1].toLowerCase(); + + let mimeIcon = 'generic'; + + if (mimePrefix === 'video') { + mimeIcon = 'video'; + } else { + mimeIcon = mimeMapping[mimeSuffix] || 'generic';; + } + + return `/images/asset_${mimeIcon}.png`; } \ No newline at end of file diff --git a/src/Squidex/app/shared/services/assets.service.ts b/src/Squidex/app/shared/services/assets.service.ts index 4a53f21ec..ad9b8e4fb 100644 --- a/src/Squidex/app/shared/services/assets.service.ts +++ b/src/Squidex/app/shared/services/assets.service.ts @@ -116,7 +116,7 @@ export class AssetsService { 'Authorization': `${this.authService.user.user.token_type} ${this.authService.user.user.access_token}` }); - content.append('files', file); + content.append('file', file); this.http .post(url, content, { headers }) diff --git a/src/Squidex/wwwroot/images/asset_doc.png b/src/Squidex/wwwroot/images/asset_doc.png new file mode 100644 index 0000000000000000000000000000000000000000..08fb7de74e84392acd22a22827263480cf586e23 GIT binary patch literal 911 zcmeAS@N?(olHy`uVBq!ia0vp^DImII>x7lo!=05bR|ofDaMp5f4cARCAjm)rqLffP(RFF5(U(4-SU_W%F?fqEI5 z4=3$-z}kM?YWaRR%Z<#H+ta zmV-!ZVtUf1xj##oCT*A~Flz#X@%aau@Bh06P7f{>fADI>RBy}9e`h1>LKmE7n=2~% zcU4iG?!)iDK1B8J(3rg7&0T5Rr93GP`!Y4B2AR2dTe<^@m1!-h%yUD)nWnUV5N1xM)Me{7=F5TL8cUtz1jm)f0 zT!)X!Ys7G9&Vivf9Z`RHIt@5Teo`4bH9JtPwrRl;(GE_@#}NrD<1!>cedpi z=AD|^FKqd>a5nSJ?sapv-Z*vh4xfd<7axXu7p61UANqXsKBsuW5uZNAD4P>|XE3i# zsJzUc^zehi+}U~?wh1gt=Cze$(H6dU;n+ErKfE(0tSped=%y6od?e;X^Na0FKR14T zd?jhukArW&o$@k&6~%b|=;>q64E)m5*`n=I)LJI`DY?w&FKqe$MTh%4>;EhN-+H_h z@er+F;{VG2@8|E4KYuy>%MxEDZuqG@^5h4v&b0UY=V-33S@WzwFZ=2GbXV6+^CO!0 VxDuyNeG5!|44$rjF6*2UngATqz<&S$ literal 0 HcmV?d00001 diff --git a/src/Squidex/wwwroot/images/asset_docx.png b/src/Squidex/wwwroot/images/asset_docx.png new file mode 100644 index 0000000000000000000000000000000000000000..5a31c92da66d505e4cd2242dcdfea3a75ebb2dce GIT binary patch literal 958 zcmeAS@N?(olHy`uVBq!ia0vp^DImM$_1e*7lfx?5T15HXd0L? zwtFAe-G0CH`rFXWckjIak+tVh)VBMMt8V`N_y6LnZ%3bgDcJuwci*Fg9ru0K-*H)e zOMAi9_doueefjmk<4>FKepqnj&4jZrYmYt++;~@I?&a6ten0#2YsdYM(=NRJ{`>EZ zci&gvdKbFp`jX3Qe*!%zS`y?J%&_X1-IvF|bneft`%?YuYx%x`VpvSqoR-e$=^vH9mkd-)e6O)P(&!=`9IbxOC7k&EX}7a(y`&1K3IA14>j zMkXgNVJ9wQr!K2U7Q0_somW`0LV^E#opC%1m+pQ3o(#6Sr8_UEITWS{ELtQ`6n(7d zcG2B5(Tx6pnClFC+ZJ@KaVdbqPN~Q&8a&6Zn3_-9$U*BZ5iqP>kE{Y zAR`&UohScR?fZV*!Q-I|P88_0C8bneUQ}S>j>!r;gB8qos&xE%S&V9wL5__p*W-B& zB5o}}^h>5+=H6@(PrNLtL2 z(?rhl8^^A(?BSd-VWz>WMGdnIN)n3|xi8MF{*=D@)}jMjKc0(yX~uFv zr!{+C@ATg5&e8|2eUbYi;LRcH;M>6`e@T4x-OV~Z;(x;buUDQKtPm-)bp1#Dzx?l` z-+XO7{9B}2@bQOrdgqdM+5OmE^Jm_JqMo4e_;vMjmM%S6$1EHwe8TTif)p@~GI+ZB KxvXHq)#1KI0$?1BjHKYD80i4zTAL7>#Dx9@=3 z7q42o_vp!$8@D`q^7Q`Wr&n&>5hz_W7ifoANswPK!>VI;UmpLcovnTU-Z=-x=NFcA z8t^f#+h;PFfq~K5)5S3))h=H%I@;Y zXiVL=By!r@*J7uGj+a(17wrG+l|HfB_n<^`jh9z=a>o*#CLnI^STaRHNi)8iOc=hT~&qJ9bpp{XBg-`{W2HXfQqmg0X@?%yB!_aAtBfyA=n`y*&!j=AtBizA=n@w-yk5^AtA;eAJ-uv*dQR=BqZ4! z9M~Nl(jFezAtCBQLjV8&w;UY99v=Vw{p^pA;7v{X;oMF)`XJE7>O}{O<1h<>mO;*!IfG^uE6Hxw-9_nCEtO{`&g; z@$vcF+w!uq?wp+Ki;L=ri0OcU=zM(UZ*S#lYT;8;-$X>-KtTTY_xtPX`{?NCd3on_ zbmV1a^~A*DT3Ya|tnjO=?xCUXpP%cDjN@Kjmn%zq0000LbW%=J0My%D`1V%Mi|n&- z5bo);ZyXyM5ovOR5C8xJE=fc|RA}Dq+Sd-#Fcg4c_TF&#Ax&Dk_n@2ZT{iFkHaJjP zArObg@m>F$sJZ#%xV9q2S1w)p+0#2$Z_595?RrGx)=AKjm)D~iBIh8Hlm~auLn0~n z??)wU&C@RuQdaM-;^dDT9}3!5dK&8zckoWgH@kVuMuSqjcb_D2fdtb()e0=R{Ggkhgfb zCVUe`u|l>Gc#s!Gu|hH)rk=9HSR#_d)D%UrL?)Qpq9~R~r2x!CQ7jQD5zf1!*dkkC zCyHW=41ju7Q7n;E4bxZ@#SWPhwytQ36_S8@7lxwr$Og$mq77b7L9SXLc@J`x4P;a9 zkTe7$Q()o_N&ehZ-a=tvgJ_%ZJiPXxQFVljVIyHXVCV?R!sp|{_hwFz9N-^z;{aB^ z3#1K=BUpe83$pK9*)Eh~L6#ou#UE}sRBvJ`RgIg3&#@pwSS9+ufYLNCGBPgN-Q+#+Ar7R{ zJ3gW0SO3@SA8)oK2Bg~NXT@}0PG#O6?w0M5)Gi=w{bupU3N00D#ZgtPk$r=(;#PTs zut1jYG5!4ixZCqR@8* zK|e1X`?_|{*EO@=*ChVBasJ2QZJ#FB{{Qp)_{_O)fZh-;3GxeOxNG(3rH&5 zUpr;9vhfiwOGOZ2=CWo=vGWnHl@dw;GBA`g!6d7V|4RR?ID^hbjv~FM1vSJxcKi1> z&i|)XC+@PhElo(HjpJ(`cgvogY>i1hG3SEU&+~hJIxe=^!O-ncrv0)jv-ONGDF&RG zzWPPxPLFyTBjj)2C+ zrZ^Fi$DzAxzUNNZ@qbB-sMqp?JyL?ppcfv`Rm@#fBwoa zy}B!=DJHxzLF~-OJIOi@OacK33uZEShI9r=yx!`R%V-podMb_2L`-34lGnvFzA4-j zmT3hVuzzCdI6mQ(44eCt(?`$i?W*40dw-7hHID|BUzShYEi~pgi*w{_8|->*S#fpR z2ey;XAGE4CW&!24|H?V>`~k1ApcCh_hJ)v)@tm}J^JbIPK7oatvztv7HJy^*R91dh zaeuVo+bfyGoc;yBR=d5Qb&sjCbghj*&zJ{-4q~7K+xstzlnVH?#g|v_E?fJH_mt>^+aD)7y8&|pgQu&X%Q~loCIB@;_DBE# literal 0 HcmV?d00001 diff --git a/src/Squidex/wwwroot/images/asset_pptx.png b/src/Squidex/wwwroot/images/asset_pptx.png new file mode 100644 index 0000000000000000000000000000000000000000..1d48321115359ea7708725c3e7a541808d8ec33b GIT binary patch literal 1058 zcmV+-1l{|IP)|#OfV?pg=LG5Be@MAvhVnXa-L)lR=?PEagWI*g< zLI3~%^?6yxLL=>GM*HE~^rMjNY);foE%KIt?{`}Ew5087OZ?;A?QT!|+|~Kc#Q**L z``OX=x~%=}>HOv3^{=1xsFnTe=JcS9{_^nq-q`ue!1b(~_{Y2Vzp?hUrt_MG?r>86 z_x1VE#{KZ^@{)S-jdkvFRrk58@Pumbd|~j0Z1kdz+1{H*0000IbW%=J0My%D{P^}( z?a#D}5O5mlZydt}%qIW<0{=-wK~!ko?b_>Z+CUJ7;nK7@H*IHHgKKPX;sh{f%sC{v z|GTO=Wda|H>z%CBAG}g5<=3OxT|fqZI(BT`&#S^I{ac?;42?4pvXL(*W(H9X0h zeVz|7Qod0>#7Mc!i5MvtIT0hJV1S5X2@%C2B8o*s6ceN#SK3o*q*pJA<5nE^e4nV? z4ge7)H{v&_+;Z#P^g1W1m5H3%f}$BrB8m}0wWk!J_F|3DqSC!5KnxT?9-r$)s`sC} zHD`lRzvTd8q6iZ98v)hZ%j*VlE1?GnC^?raZMj^uMS4j&9s&Z23h~B7r7<9&6qKBZ z+u}V144$~O*Y|wLfEkosBdtz*6>=fLh(Q5^TaF58Fr%_(q%~DY$h*pTscc^#)e3Kl zG{}(O^thcx=Gb-@C=gEwSuruBnHeHy3uh^9)=KgfNQWhv<#i6Jqd+2NoP}QJkRklLw|(5!*gJs@@++GZs)+N$~Q=y0}Dxm zlwgj7y(mAla1Lw~s1O>+9Ae^~xuVi<>V}EO zt{!>#;@-P&pMET^+w<=E=f^i*_b08}yXgFx?YGwU9$D3SXnOAE2`THlV^)89`|ZQa zFSpM=y|n+{@r~E#SL~dUxv@84&Fe=W?w)^kV$=0w>#rVIesSxxle3Gr-M{=|=bSUY zfBq>d_}2h*u}DdfUogWeyDyKA{eD=hGn?_txeJFnU9I_c*Ut@SU|>Ap>EalYaqsP| z<9UZ1L|Pw)Sglk~wK>*s(Cla{U*&)C?ui}e)Pjn1^Oksg)=yhb9)PR`k6+5 zm8!e`H`H{|-31?#w_m?Hg-yXBPD}IbES{B1wkSAjX_hJmd9FMuxRXiI$yIRSqK*Jd z^B{qRL5zmQ3=?~QO&0l_ka(F zycBSG)_!@1nq|Y=IUNy-O$8E9T*@Z@X{k`{eLAfpMsdKb2-blx9AXV?UKDt}Num1aMd zW9mR*Rt~{}0006xNklrUH15Qbq=S_qWV)41JjZ6`DdiA~N& z&b|M;VzajL3B&@k&Ilp){Sn1~{Ae_?WXZb|3KjkQQrn}K^mARgkSd}ktFN}Z4Jl^PR)B8WhdM4(hhG8Y1ckLas5ks8tBhD@tLez9;M zQ23L~n%R6Xr^HxvGt{L2I}j**gvN`r78OX_Xvl#;VIVrcq7u=|^L#mTB2d`K?Qqbs zh!tli=b00M!bGxsHoepZ($hrL=(!OnJY+d08jUH5udi2=eASId^) z0;MwYuNg(%jd5m-#;HmT0;K{1qflV1x1SJ{3J8o+fnneMp;SaV4RhPe)cj2BKCDsb zL{NMY%n|F>TIpgA1jPrz9EmO;z~S2H5IGe-f;q@!XTcl@iXQ^QDwxBe_#iM!1%^Xm zBQQ2fvoPEW7s1XEE*#1?E;3E6!K%hX><(g0(g^i6$sq_{Z0n06Dzd471mw*lJ!R_& zBKQ}O*FdmjK?FApNI(J-kbp3H*GPRkNc~CVXgkOe|D!*@1NQL+eqRsS2Qm2VbB~np o+k@fk7>8qggxZheolwYj2UMKw9}b!8!2kdN07*qoM6N<$f_S@@AOHXW literal 0 HcmV?d00001 diff --git a/src/Squidex/wwwroot/images/asset_xlsx.png b/src/Squidex/wwwroot/images/asset_xlsx.png new file mode 100644 index 0000000000000000000000000000000000000000..d28ad0789f465cfacb036118301aebd71562b274 GIT binary patch literal 905 zcmV;419tq0P)Kb2-bm1aMbWDt}Num1aMd zWayV}aP*v6mAvz5iGbhD9Q zr+`tPbn^K1#^zY5%*Rj;WtSV}_U+Gb5VMRL_vy5X9RHyNT|9 z8VrOGU*(2y=IZzVP+i-okEr9eJI<(zvi}#BU-_qDWC_W8V6$a?KCir|f9uU5(>SUU z`Wy20kY^AXAYWL@>9HBYQclPWVJROC5tdRjL|9725P>3yK#@eCltY;f-K8iHjsk?H31IRuJ1;)N^PA>P9xyr1ivhee!27z&VJuxzAAk~?oLB8tM{V-7J_fW+avJ@hif z_w=Rd(|F@w`TGb*0dhYj8cj*5^?K6lPkL%Z&WdQW+D8})5V!3SMI8zp$I;hKZ%Z^w z*58f_5GVp~d`^)>phzN6BoQc*2oy;KN_hl*`}%b6${=|~5`iL#K#@eCNFq=q5h!9v zJZI03)yP5X?ckWEL0*3KM~`Q(zz{ECj|*fl;6sBQSm`OBhcprU-V9 z02cJY6zOzR!>EcOlI%daB#qG4BS9ozpwa?S#Hpt_7O}{SM|>yE7esJpk^g~U$$|(T zXc3E8#3B|k=v5=N=RsQ*6lrXEpDD2|AQY|4= fK3yExY-v9MTlDUH3492T00000NkvXXu0mjf^ggH+ literal 0 HcmV?d00001 diff --git a/tests/Squidex.Write.Tests/Apps/AppCommandHandlerTests.cs b/tests/Squidex.Write.Tests/Apps/AppCommandHandlerTests.cs index c99271606..367911618 100644 --- a/tests/Squidex.Write.Tests/Apps/AppCommandHandlerTests.cs +++ b/tests/Squidex.Write.Tests/Apps/AppCommandHandlerTests.cs @@ -39,7 +39,7 @@ namespace Squidex.Write.Apps public AppCommandHandlerTests() { - app = new AppDomainObject(AppId, 0); + app = new AppDomainObject(AppId, -1); sut = new AppCommandHandler(Handler, appRepository.Object, userRepository.Object, keyGenerator.Object); } @@ -47,7 +47,7 @@ namespace Squidex.Write.Apps [Fact] public async Task Create_should_throw_if_a_name_with_same_name_already_exists() { - var context = CreateContextForCommand(new CreateApp { Name = AppName, AggregateId = AppId }); + var context = CreateContextForCommand(new CreateApp { Name = AppName, AppId = AppId }); appRepository.Setup(x => x.FindAppAsync(AppName)) .Returns(Task.FromResult(new Mock().Object)) @@ -64,7 +64,7 @@ namespace Squidex.Write.Apps [Fact] public async Task Create_should_create_app_if_name_is_free() { - var context = CreateContextForCommand(new CreateApp { Name = AppName, AggregateId = AppId }); + var context = CreateContextForCommand(new CreateApp { Name = AppName, AppId = AppId }); appRepository.Setup(x => x.FindAppAsync(AppName)) .Returns(Task.FromResult(null)) diff --git a/tests/Squidex.Write.Tests/Apps/AppDomainObjectTests.cs b/tests/Squidex.Write.Tests/Apps/AppDomainObjectTests.cs index 121ab3787..750f1c8c2 100644 --- a/tests/Squidex.Write.Tests/Apps/AppDomainObjectTests.cs +++ b/tests/Squidex.Write.Tests/Apps/AppDomainObjectTests.cs @@ -56,7 +56,7 @@ namespace Squidex.Write.Apps [Fact] public void Create_should_specify_name_and_owner() { - sut.Create(CreateCommand(new CreateApp { Name = AppName, Actor = User, AggregateId = AppId })); + sut.Create(CreateCommand(new CreateApp { Name = AppName, Actor = User, AppId = AppId })); Assert.Equal(AppName, sut.Name); diff --git a/tests/Squidex.Write.Tests/Assets/AssetCommandHandlerTests.cs b/tests/Squidex.Write.Tests/Assets/AssetCommandHandlerTests.cs index 26367ea4a..18a770f2d 100644 --- a/tests/Squidex.Write.Tests/Assets/AssetCommandHandlerTests.cs +++ b/tests/Squidex.Write.Tests/Assets/AssetCommandHandlerTests.cs @@ -7,11 +7,15 @@ // ========================================================================== using System; +using System.IO; using System.Threading.Tasks; +using Squidex.Infrastructure.Assets; using Squidex.Infrastructure.CQRS.Commands; using Squidex.Write.Assets.Commands; using Squidex.Write.TestHelpers; using Xunit; +using Moq; +using Squidex.Infrastructure.Tasks; // ReSharper disable ConvertToConstant.Local @@ -19,24 +23,31 @@ namespace Squidex.Write.Assets { public class AssetCommandHandlerTests : HandlerTestBase { + private readonly Mock assetThumbnailGenerator = new Mock(); + private readonly Mock assetStore = new Mock(); private readonly AssetCommandHandler sut; private readonly AssetDomainObject asset; private readonly Guid assetId = Guid.NewGuid(); - private readonly string fileName = "my-image.png"; - private readonly string mimeType = "image/png"; - private readonly long fileSize = 1024; + private readonly Stream stream = new MemoryStream(); + private readonly ImageInfo image = new ImageInfo(2048, 2048); + private readonly AssetFile file; public AssetCommandHandlerTests() { - asset = new AssetDomainObject(assetId, 0); + file = new AssetFile("my-image.png", "image/png", 1024, () => stream); - sut = new AssetCommandHandler(Handler); + asset = new AssetDomainObject(assetId, -1); + + sut = new AssetCommandHandler(Handler, assetStore.Object, assetThumbnailGenerator.Object); } [Fact] public async Task Create_should_create_asset() { - var context = CreateContextForCommand(new CreateAsset { AssetId = assetId, FileName = fileName, FileSize = fileSize, MimeType = mimeType }); + assetStore.Setup(x => x.UploadAssetAsync(assetId, 0, stream, null)).Returns(TaskHelper.Done).Verifiable(); + assetThumbnailGenerator.Setup(x => x.GetImageInfoAsync(stream)).Returns(Task.FromResult(image)).Verifiable(); + + var context = CreateContextForCommand(new CreateAsset { AssetId = assetId, File = file }); await TestCreate(asset, async _ => { @@ -44,19 +55,28 @@ namespace Squidex.Write.Assets }); Assert.Equal(assetId, context.Result>().IdOrValue); + + assetStore.VerifyAll(); + assetThumbnailGenerator.VerifyAll(); } [Fact] public async Task Update_should_update_domain_object() { + assetStore.Setup(x => x.UploadAssetAsync(assetId, 1, stream, null)).Returns(TaskHelper.Done).Verifiable(); + assetThumbnailGenerator.Setup(x => x.GetImageInfoAsync(stream)).Returns(Task.FromResult(image)).Verifiable(); + CreateAsset(); - var context = CreateContextForCommand(new UpdateAsset { AssetId = assetId, FileSize = fileSize, MimeType = mimeType }); + var context = CreateContextForCommand(new UpdateAsset { AssetId = assetId, File = file }); await TestUpdate(asset, async _ => { await sut.HandleAsync(context); }); + + assetStore.VerifyAll(); + assetThumbnailGenerator.VerifyAll(); } [Fact] @@ -87,7 +107,7 @@ namespace Squidex.Write.Assets private void CreateAsset() { - asset.Create(new CreateAsset { FileName = fileName, FileSize = fileSize, MimeType = mimeType }); + asset.Create(new CreateAsset { File = file }); } } } diff --git a/tests/Squidex.Write.Tests/Assets/AssetDomainObjectTests.cs b/tests/Squidex.Write.Tests/Assets/AssetDomainObjectTests.cs index e6ef96af5..571987b7b 100644 --- a/tests/Squidex.Write.Tests/Assets/AssetDomainObjectTests.cs +++ b/tests/Squidex.Write.Tests/Assets/AssetDomainObjectTests.cs @@ -7,8 +7,10 @@ // ========================================================================== using System; +using System.IO; using Squidex.Events.Assets; using Squidex.Infrastructure; +using Squidex.Infrastructure.Assets; using Squidex.Infrastructure.CQRS; using Squidex.Write.Assets.Commands; using Squidex.Write.TestHelpers; @@ -21,9 +23,8 @@ namespace Squidex.Write.Assets public class AssetDomainObjectTests : HandlerTestBase { private readonly AssetDomainObject sut; - private readonly string fileName = "my-image.png"; - private readonly string mimeType = "image/png"; - private readonly long fileSize = 1024; + private readonly ImageInfo image = new ImageInfo(2048, 2048); + private readonly AssetFile file = new AssetFile("my-image.png", "image/png", 1024, () => new MemoryStream()); public Guid AssetId { get; } = Guid.NewGuid(); @@ -35,22 +36,30 @@ namespace Squidex.Write.Assets [Fact] public void Create_should_throw_if_created() { - sut.Create(new CreateAsset { FileName = fileName, FileSize = fileSize, MimeType = mimeType }); + sut.Create(new CreateAsset { File = file }); Assert.Throws(() => { - sut.Create(CreateAssetCommand(new CreateAsset { FileName = fileName, FileSize = fileSize, MimeType = mimeType })); + sut.Create(CreateAssetCommand(new CreateAsset { File = file })); }); } [Fact] public void Create_should_create_events() { - sut.Create(CreateAssetCommand(new CreateAsset { FileName = fileName, FileSize = fileSize, MimeType = mimeType })); + sut.Create(CreateAssetCommand(new CreateAsset { File = file, ImageInfo = image })); sut.GetUncomittedEvents() .ShouldHaveSameEvents( - CreateAssetEvent(new AssetCreated { FileName = fileName, FileSize = fileSize, MimeType = mimeType }) + CreateAssetEvent(new AssetCreated + { + IsImage = true, + FileName = file.FileName, + FileSize = file.FileSize, + MimeType = file.MimeType, + PixelWidth = image.PixelWidth, + PixelHeight = image.PixelHeight + }) ); } @@ -59,7 +68,7 @@ namespace Squidex.Write.Assets { Assert.Throws(() => { - sut.Update(CreateAssetCommand(new UpdateAsset { FileSize = fileSize, MimeType = mimeType })); + sut.Update(CreateAssetCommand(new UpdateAsset { File = file })); }); } @@ -80,11 +89,18 @@ namespace Squidex.Write.Assets { CreateAsset(); - sut.Update(CreateAssetCommand(new UpdateAsset { FileSize = fileSize, MimeType = mimeType })); + sut.Update(CreateAssetCommand(new UpdateAsset { File = file, ImageInfo = image })); sut.GetUncomittedEvents() .ShouldHaveSameEvents( - CreateAssetEvent(new AssetUpdated { FileSize = fileSize, MimeType = mimeType }) + CreateAssetEvent(new AssetUpdated + { + IsImage = true, + FileSize = file.FileSize, + MimeType = file.MimeType, + PixelWidth = image.PixelWidth, + PixelHeight = image.PixelHeight + }) ); } @@ -93,7 +109,7 @@ namespace Squidex.Write.Assets { Assert.Throws(() => { - sut.Update(CreateAssetCommand(new UpdateAsset { FileSize = fileSize, MimeType = mimeType })); + sut.Rename(CreateAssetCommand(new RenameAsset { FileName = "new-file.png" })); }); } @@ -127,7 +143,7 @@ namespace Squidex.Write.Assets Assert.Throws(() => { - sut.Rename(CreateAssetCommand(new RenameAsset { FileName = fileName })); + sut.Rename(CreateAssetCommand(new RenameAsset { FileName = file.FileName })); }); } @@ -182,7 +198,7 @@ namespace Squidex.Write.Assets private void CreateAsset() { - sut.Create(CreateAssetCommand(new CreateAsset { FileName = fileName, FileSize = fileSize, MimeType = mimeType })); + sut.Create(CreateAssetCommand(new CreateAsset { File = file })); ((IAggregate)sut).ClearUncommittedEvents(); } diff --git a/tests/Squidex.Write.Tests/Contents/ContentCommandHandlerTests.cs b/tests/Squidex.Write.Tests/Contents/ContentCommandHandlerTests.cs index b530a12f8..d7867095f 100644 --- a/tests/Squidex.Write.Tests/Contents/ContentCommandHandlerTests.cs +++ b/tests/Squidex.Write.Tests/Contents/ContentCommandHandlerTests.cs @@ -43,7 +43,7 @@ namespace Squidex.Write.Contents .AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties { IsRequired = true })); - content = new ContentDomainObject(contentId, 0); + content = new ContentDomainObject(contentId, -1); sut = new ContentCommandHandler(Handler, appProvider.Object, schemaProvider.Object); diff --git a/tests/Squidex.Write.Tests/Schemas/SchemaCommandHandlerTests.cs b/tests/Squidex.Write.Tests/Schemas/SchemaCommandHandlerTests.cs index 80cd0b6a5..c6e4bfe10 100644 --- a/tests/Squidex.Write.Tests/Schemas/SchemaCommandHandlerTests.cs +++ b/tests/Squidex.Write.Tests/Schemas/SchemaCommandHandlerTests.cs @@ -32,7 +32,7 @@ namespace Squidex.Write.Schemas public SchemaCommandHandlerTests() { - schema = new SchemaDomainObject(SchemaId, 0, registry); + schema = new SchemaDomainObject(SchemaId, -1, registry); sut = new SchemaCommandHandler(Handler, schemaProvider.Object); }