Browse Source

AssetAlreadyExistsException.cs for Folder and GridFS

pull/309/head
Sebastian 8 years ago
parent
commit
950ae74f45
  1. 4
      src/Squidex.Infrastructure.Azure/Assets/AzureBlobAssetStore.cs
  2. 4
      src/Squidex.Infrastructure.GoogleCloud/Assets/GoogleCloudAssetStore.cs
  3. 34
      src/Squidex.Infrastructure.MongoDb/Assets/MongoGridFsAssetStore.cs
  4. 38
      src/Squidex.Infrastructure/Assets/AssetAlreadyExistsException.cs
  5. 17
      src/Squidex.Infrastructure/Assets/AssetNotFoundException.cs
  6. 78
      src/Squidex.Infrastructure/Assets/FolderAssetStore.cs
  7. 6
      src/Squidex.Infrastructure/Assets/IAssetStore.cs
  8. 62
      tests/Squidex.Infrastructure.Tests/Assets/AssetStoreTests.cs

4
src/Squidex.Infrastructure.Azure/Assets/AzureBlobAssetStore.cs

@ -83,7 +83,7 @@ namespace Squidex.Infrastructure.Assets
}
catch (StorageException ex) when (ex.RequestInformation.HttpStatusCode == 404)
{
throw new AssetNotFoundException($"Asset {name} not found.", ex);
throw new AssetNotFoundException(name, ex);
}
}
@ -98,7 +98,7 @@ namespace Squidex.Infrastructure.Assets
}
catch (StorageException ex) when (ex.RequestInformation.HttpStatusCode == 404)
{
throw new AssetNotFoundException($"Asset {id}, {version} not found.", ex);
throw new AssetNotFoundException($"Id={id}, Version={version}", ex);
}
}

4
src/Squidex.Infrastructure.GoogleCloud/Assets/GoogleCloudAssetStore.cs

@ -71,7 +71,7 @@ namespace Squidex.Infrastructure.Assets
}
catch (GoogleApiException ex) when (ex.HttpStatusCode == HttpStatusCode.NotFound)
{
throw new AssetNotFoundException($"Asset {name} not found.", ex);
throw new AssetNotFoundException(name, ex);
}
}
@ -85,7 +85,7 @@ namespace Squidex.Infrastructure.Assets
}
catch (GoogleApiException ex) when (ex.HttpStatusCode == HttpStatusCode.NotFound)
{
throw new AssetNotFoundException($"Asset {id}, {version} not found.", ex);
throw new AssetNotFoundException($"Id={id}, Version={version}", ex);
}
}

34
src/Squidex.Infrastructure.MongoDb/Assets/MongoGridFsAssetStore.cs

@ -9,6 +9,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.GridFS;
@ -43,20 +44,20 @@ namespace Squidex.Infrastructure.Assets
return "UNSUPPORTED";
}
public async Task CopyAsync(string name, string id, long version, string suffix, CancellationToken ct = default(CancellationToken))
public async Task CopyAsync(string sourceFileName, string id, long version, string suffix, CancellationToken ct = default(CancellationToken))
{
try
{
var target = GetFileName(id, version, suffix);
using (var readStream = await bucket.OpenDownloadStreamAsync(name, cancellationToken: ct))
using (var readStream = await bucket.OpenDownloadStreamAsync(sourceFileName, cancellationToken: ct))
{
await bucket.UploadFromStreamAsync(target, target, readStream, cancellationToken: ct);
await UploadFileCoreAsync(target, readStream, ct);
}
}
catch (GridFSFileNotFoundException ex)
{
throw new AssetNotFoundException($"Asset {name} not found.", ex);
throw new AssetNotFoundException(sourceFileName, ex);
}
}
@ -73,13 +74,13 @@ namespace Squidex.Infrastructure.Assets
}
catch (GridFSFileNotFoundException ex)
{
throw new AssetNotFoundException($"Asset {id}, {version} not found.", ex);
throw new AssetNotFoundException($"Id={id}, Version={version}", ex);
}
}
public Task UploadAsync(string name, Stream stream, CancellationToken ct = default(CancellationToken))
public Task UploadAsync(string fileName, Stream stream, CancellationToken ct = default(CancellationToken))
{
return UploadFileCoreAsync(name, stream, ct);
return UploadFileCoreAsync(fileName, stream, ct);
}
public Task UploadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken))
@ -87,9 +88,9 @@ namespace Squidex.Infrastructure.Assets
return UploadFileCoreAsync(GetFileName(id, version, suffix), stream, ct);
}
public Task DeleteAsync(string name)
public Task DeleteAsync(string fileName)
{
return DeleteCoreAsync(name);
return DeleteCoreAsync(fileName);
}
public Task DeleteAsync(string id, long version, string suffix)
@ -109,9 +110,20 @@ namespace Squidex.Infrastructure.Assets
}
}
private Task UploadFileCoreAsync(string id, Stream stream, CancellationToken ct = default(CancellationToken))
private async Task UploadFileCoreAsync(string id, Stream stream, CancellationToken ct = default(CancellationToken))
{
return bucket.UploadFromStreamAsync(id, id, stream, cancellationToken: ct);
try
{
await bucket.UploadFromStreamAsync(id, id, stream, cancellationToken: ct);
}
catch (MongoWriteException ex) when (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
{
throw new AssetAlreadyExistsException(id);
}
catch (MongoBulkWriteException<BsonDocument> ex) when (ex.WriteErrors.Any(x => x.Category == ServerErrorCategory.DuplicateKey))
{
throw new AssetAlreadyExistsException(id);
}
}
private static string GetFileName(string id, long version, string suffix)

38
src/Squidex.Infrastructure/Assets/AssetAlreadyExistsException.cs

@ -0,0 +1,38 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Runtime.Serialization;
namespace Squidex.Infrastructure.Assets
{
[Serializable]
public class AssetAlreadyExistsException : Exception
{
public AssetAlreadyExistsException(string fileName)
: base(FormatMessage(fileName))
{
}
public AssetAlreadyExistsException(string fileName, Exception inner)
: base(FormatMessage(fileName), inner)
{
}
protected AssetAlreadyExistsException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
private static string FormatMessage(string fileName)
{
Guard.NotNullOrEmpty(fileName, nameof(fileName));
return $"An asset with name '{fileName}' already not exists.";
}
}
}

17
src/Squidex.Infrastructure/Assets/AssetNotFoundException.cs

@ -13,23 +13,26 @@ namespace Squidex.Infrastructure.Assets
[Serializable]
public class AssetNotFoundException : Exception
{
public AssetNotFoundException()
public AssetNotFoundException(string fileName)
: base(FormatMessage(fileName))
{
}
public AssetNotFoundException(string message)
: base(message)
public AssetNotFoundException(string fileName, Exception inner)
: base(FormatMessage(fileName), inner)
{
}
public AssetNotFoundException(string message, Exception inner)
: base(message, inner)
protected AssetNotFoundException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
protected AssetNotFoundException(SerializationInfo info, StreamingContext context)
: base(info, context)
private static string FormatMessage(string fileName)
{
Guard.NotNullOrEmpty(fileName, nameof(fileName));
return $"An asset with name '{fileName}' does not exist.";
}
}
}

78
src/Squidex.Infrastructure/Assets/FolderAssetStore.cs

@ -59,26 +59,6 @@ namespace Squidex.Infrastructure.Assets
return file.FullName;
}
public async Task UploadAsync(string name, Stream stream, CancellationToken ct = default(CancellationToken))
{
var file = GetFile(name);
using (var fileStream = file.OpenWrite())
{
await stream.CopyToAsync(fileStream, BufferSize, ct);
}
}
public async Task UploadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken))
{
var file = GetFile(id, version, suffix);
using (var fileStream = file.OpenWrite())
{
await stream.CopyToAsync(fileStream, BufferSize, ct);
}
}
public async Task DownloadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken))
{
var file = GetFile(id, version, suffix);
@ -92,44 +72,74 @@ namespace Squidex.Infrastructure.Assets
}
catch (FileNotFoundException ex)
{
throw new AssetNotFoundException($"Asset {id}, {version} not found.", ex);
throw new AssetNotFoundException($"Id={id}, Version={version}", ex);
}
}
public Task CopyAsync(string name, string id, long version, string suffix, CancellationToken ct = default(CancellationToken))
public Task CopyAsync(string sourceFileName, string id, long version, string suffix, CancellationToken ct = default(CancellationToken))
{
var targetFile = GetFile(id, version, suffix);
try
{
var file = GetFile(name);
var file = GetFile(sourceFileName);
file.CopyTo(GetPath(id, version, suffix));
file.CopyTo(targetFile.FullName);
return TaskHelper.Done;
}
catch (IOException) when (targetFile.Exists)
{
throw new AssetAlreadyExistsException(targetFile.Name);
}
catch (FileNotFoundException ex)
{
throw new AssetNotFoundException($"Asset {name} not found.", ex);
throw new AssetNotFoundException(sourceFileName, ex);
}
}
public Task DeleteAsync(string id, long version, string suffix)
public Task UploadAsync(string fileName, Stream stream, CancellationToken ct = default(CancellationToken))
{
var file = GetFile(id, version, suffix);
return UploadCoreAsync(GetFile(fileName), stream, ct);
}
file.Delete();
public Task UploadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken))
{
return UploadCoreAsync(GetFile(id, version, suffix), stream, ct);
}
return TaskHelper.Done;
public Task DeleteAsync(string id, long version, string suffix)
{
return DeleteFileCoreAsync(GetFile(id, version, suffix));
}
public Task DeleteAsync(string name)
public Task DeleteAsync(string fileName)
{
var file = GetFile(name);
return DeleteFileCoreAsync(GetFile(fileName));
}
private static Task DeleteFileCoreAsync(FileInfo file)
{
file.Delete();
return TaskHelper.Done;
}
private async Task UploadCoreAsync(FileInfo file, Stream stream, CancellationToken ct)
{
try
{
using (var fileStream = file.Open(FileMode.CreateNew, FileAccess.Write))
{
await stream.CopyToAsync(fileStream, BufferSize, ct);
}
}
catch (IOException) when (file.Exists)
{
throw new AssetAlreadyExistsException(file.Name);
}
}
private FileInfo GetFile(string id, long version, string suffix)
{
Guard.NotNullOrEmpty(id, nameof(id));
@ -137,11 +147,11 @@ namespace Squidex.Infrastructure.Assets
return GetFile(GetPath(id, version, suffix));
}
private FileInfo GetFile(string name)
private FileInfo GetFile(string fileName)
{
Guard.NotNullOrEmpty(name, nameof(name));
Guard.NotNullOrEmpty(fileName, nameof(fileName));
return new FileInfo(GetPath(name));
return new FileInfo(GetPath(fileName));
}
private string GetPath(string name)

6
src/Squidex.Infrastructure/Assets/IAssetStore.cs

@ -15,15 +15,15 @@ namespace Squidex.Infrastructure.Assets
{
string GenerateSourceUrl(string id, long version, string suffix);
Task CopyAsync(string name, string id, long version, string suffix, CancellationToken ct = default(CancellationToken));
Task CopyAsync(string sourceFileName, string id, long version, string suffix, CancellationToken ct = default(CancellationToken));
Task DownloadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken));
Task UploadAsync(string name, Stream stream, CancellationToken ct = default(CancellationToken));
Task UploadAsync(string fileName, Stream stream, CancellationToken ct = default(CancellationToken));
Task UploadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken));
Task DeleteAsync(string name);
Task DeleteAsync(string fileName);
Task DeleteAsync(string id, long version, string suffix);
}

62
tests/Squidex.Infrastructure.Tests/Assets/AssetStoreTests.cs

@ -14,11 +14,16 @@ namespace Squidex.Infrastructure.Assets
{
public abstract class AssetStoreTests<T> : IDisposable where T : IAssetStore
{
private readonly MemoryStream assetData = new MemoryStream(new byte[] { 0x1, 0x2, 0x3, 0x4 });
private readonly string assetId = Guid.NewGuid().ToString();
private readonly string tempId = Guid.NewGuid().ToString();
private readonly Lazy<T> sut;
protected AssetStoreTests()
{
sut = new Lazy<T>(CreateStore);
((IInitializable)Sut).Initialize();
}
protected T Sut
@ -33,27 +38,18 @@ namespace Squidex.Infrastructure.Assets
[Fact]
public virtual Task Should_throw_exception_if_asset_to_download_is_not_found()
{
((IInitializable)Sut).Initialize();
return Assert.ThrowsAsync<AssetNotFoundException>(() => Sut.DownloadAsync(Id(), 1, "suffix", new MemoryStream()));
return Assert.ThrowsAsync<AssetNotFoundException>(() => Sut.DownloadAsync(assetId, 1, "suffix", new MemoryStream()));
}
[Fact]
public Task Should_throw_exception_if_asset_to_copy_is_not_found()
{
((IInitializable)Sut).Initialize();
return Assert.ThrowsAsync<AssetNotFoundException>(() => Sut.CopyAsync(Id(), Id(), 1, null));
return Assert.ThrowsAsync<AssetNotFoundException>(() => Sut.CopyAsync(tempId, assetId, 1, null));
}
[Fact]
public async Task Should_read_and_write_file()
{
((IInitializable)Sut).Initialize();
var assetId = Id();
var assetData = new MemoryStream(new byte[] { 0x1, 0x2, 0x3, 0x4 });
await Sut.UploadAsync(assetId, 1, "suffix", assetData);
var readData = new MemoryStream();
@ -64,15 +60,16 @@ namespace Squidex.Infrastructure.Assets
}
[Fact]
public virtual async Task Should_commit_temporary_file()
public async Task Should_throw_exception_when_file_to_write_already_exists()
{
((IInitializable)Sut).Initialize();
var tempId = Id();
await Sut.UploadAsync(assetId, 1, "suffix", assetData);
var assetId = Id();
var assetData = new MemoryStream(new byte[] { 0x1, 0x2, 0x3, 0x4 });
await Assert.ThrowsAsync<AssetAlreadyExistsException>(() => Sut.UploadAsync(assetId, 1, "suffix", assetData));
}
[Fact]
public virtual async Task Should_read_and_write_temporary_file()
{
await Sut.UploadAsync(tempId, assetData);
await Sut.CopyAsync(tempId, assetId, 1, "suffix");
@ -84,14 +81,26 @@ namespace Squidex.Infrastructure.Assets
}
[Fact]
public async Task Should_ignore_when_deleting_twice_by_name()
public async Task Should_throw_exception_when_temporary_file_to_write_already_exists()
{
((IInitializable)Sut).Initialize();
await Sut.UploadAsync(tempId, assetData);
await Sut.CopyAsync(tempId, assetId, 1, "suffix");
var tempId = Id();
await Assert.ThrowsAsync<AssetAlreadyExistsException>(() => Sut.UploadAsync(tempId, assetData));
}
var assetData = new MemoryStream(new byte[] { 0x1, 0x2, 0x3, 0x4 });
[Fact]
public async Task Should_throw_exception_when_target_file_to_copy_to_already_exists()
{
await Sut.UploadAsync(tempId, assetData);
await Sut.CopyAsync(tempId, assetId, 1, "suffix");
await Assert.ThrowsAsync<AssetAlreadyExistsException>(() => Sut.CopyAsync(tempId, assetId, 1, "suffix"));
}
[Fact]
public async Task Should_ignore_when_deleting_twice_by_name()
{
await Sut.UploadAsync(tempId, assetData);
await Sut.DeleteAsync(tempId);
await Sut.DeleteAsync(tempId);
@ -100,20 +109,9 @@ namespace Squidex.Infrastructure.Assets
[Fact]
public async Task Should_ignore_when_deleting_twice_by_id()
{
((IInitializable)Sut).Initialize();
var tempId = Id();
var assetData = new MemoryStream(new byte[] { 0x1, 0x2, 0x3, 0x4 });
await Sut.UploadAsync(tempId, 0, null, assetData);
await Sut.DeleteAsync(tempId, 0, null);
await Sut.DeleteAsync(tempId, 0, null);
}
protected static string Id()
{
return Guid.NewGuid().ToString();
}
}
}

Loading…
Cancel
Save