Browse Source

Full support of AssetAlreadyExistsException

pull/309/head
Sebastian 8 years ago
parent
commit
77892b9caf
  1. 71
      src/Squidex.Infrastructure.Azure/Assets/AzureBlobAssetStore.cs
  2. 66
      src/Squidex.Infrastructure.GoogleCloud/Assets/GoogleCloudAssetStore.cs
  3. 14
      src/Squidex.Infrastructure.MongoDb/Assets/MongoGridFsAssetStore.cs
  4. 8
      src/Squidex.Infrastructure/Assets/FolderAssetStore.cs
  5. 5
      tests/Squidex.Infrastructure.Tests/Assets/AssetStoreTests.cs
  6. 7
      tests/Squidex.Infrastructure.Tests/Assets/AzureBlobAssetStoreTests.cs
  7. 8
      tests/Squidex.Infrastructure.Tests/Assets/FolderAssetStoreTests.cs
  8. 7
      tests/Squidex.Infrastructure.Tests/Assets/GoogleCloudAssetStoreTests.cs
  9. 5
      tests/Squidex.Infrastructure.Tests/Assets/MongoGridFsAssetStoreTests.cs

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

@ -57,44 +57,47 @@ namespace Squidex.Infrastructure.Assets
return new Uri(blobContainer.StorageUri.PrimaryUri, $"/{containerName}/{blobName}").ToString();
}
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))
{
var blobName = GetObjectName(id, version, suffix);
var blobRef = blobContainer.GetBlobReference(blobName);
var targetName = GetObjectName(id, version, suffix);
var targetBlob = blobContainer.GetBlobReference(targetName);
var tempBlob = blobContainer.GetBlockBlobReference(name);
var sourceBlob = blobContainer.GetBlockBlobReference(sourceFileName);
try
{
await blobRef.StartCopyAsync(tempBlob.Uri, null, null, null, null, ct);
await targetBlob.StartCopyAsync(sourceBlob.Uri, null, AccessCondition.GenerateIfNotExistsCondition(), null, null, ct);
while (blobRef.CopyState.Status == CopyStatus.Pending)
while (targetBlob.CopyState.Status == CopyStatus.Pending)
{
ct.ThrowIfCancellationRequested();
await Task.Delay(50);
await blobRef.FetchAttributesAsync(null, null, null, ct);
await targetBlob.FetchAttributesAsync(null, null, null, ct);
}
if (blobRef.CopyState.Status != CopyStatus.Success)
if (targetBlob.CopyState.Status != CopyStatus.Success)
{
throw new StorageException($"Copy of temporary file failed: {blobRef.CopyState.Status}");
throw new StorageException($"Copy of temporary file failed: {targetBlob.CopyState.Status}");
}
}
catch (StorageException ex) when (ex.RequestInformation.HttpStatusCode == 409)
{
throw new AssetAlreadyExistsException(targetName);
}
catch (StorageException ex) when (ex.RequestInformation.HttpStatusCode == 404)
{
throw new AssetNotFoundException(name, ex);
throw new AssetNotFoundException(sourceFileName, ex);
}
}
public async Task DownloadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken))
{
var blobName = GetObjectName(id, version, suffix);
var blobRef = blobContainer.GetBlockBlobReference(blobName);
var blob = blobContainer.GetBlockBlobReference(GetObjectName(id, version, suffix));
try
{
await blobRef.DownloadToStreamAsync(stream, null, null, null, ct);
await blob.DownloadToStreamAsync(stream, null, null, null, ct);
}
catch (StorageException ex) when (ex.RequestInformation.HttpStatusCode == 404)
{
@ -102,37 +105,45 @@ namespace Squidex.Infrastructure.Assets
}
}
public async Task UploadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken))
public Task UploadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken))
{
var blobName = GetObjectName(id, version, suffix);
var blobRef = blobContainer.GetBlockBlobReference(blobName);
blobRef.Metadata[AssetVersion] = version.ToString();
blobRef.Metadata[AssetId] = id;
return UploadCoreAsync(GetObjectName(id, version, suffix), stream, ct);
}
await blobRef.UploadFromStreamAsync(stream, null, null, null, ct);
await blobRef.SetMetadataAsync();
public Task UploadAsync(string fileName, Stream stream, CancellationToken ct = default(CancellationToken))
{
return UploadCoreAsync(fileName, stream, ct);
}
public Task UploadAsync(string name, Stream stream, CancellationToken ct = default(CancellationToken))
public Task DeleteAsync(string id, long version, string suffix)
{
var tempBlob = blobContainer.GetBlockBlobReference(name);
return DeleteCoreAsync(GetObjectName(id, version, suffix));
}
return tempBlob.UploadFromStreamAsync(stream, null, null, null, ct);
public Task DeleteAsync(string fileName)
{
return DeleteCoreAsync(fileName);
}
public Task DeleteAsync(string name)
private Task DeleteCoreAsync(string blobName)
{
var tempBlob = blobContainer.GetBlockBlobReference(name);
var blob = blobContainer.GetBlockBlobReference(blobName);
return tempBlob.DeleteIfExistsAsync();
return blob.DeleteIfExistsAsync();
}
public Task DeleteAsync(string id, long version, string suffix)
private async Task UploadCoreAsync(string blobName, Stream stream, CancellationToken ct)
{
var tempBlob = blobContainer.GetBlockBlobReference(GetObjectName(id, version, suffix));
try
{
var tempBlob = blobContainer.GetBlockBlobReference(blobName);
return tempBlob.DeleteIfExistsAsync();
await tempBlob.UploadFromStreamAsync(stream, AccessCondition.GenerateIfNotExistsCondition(), null, null, ct);
}
catch (StorageException ex) when (ex.RequestInformation.HttpStatusCode == 409)
{
throw new AssetAlreadyExistsException(blobName);
}
}
private string GetObjectName(string id, long version, string suffix)

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

@ -18,6 +18,8 @@ namespace Squidex.Infrastructure.Assets
{
public sealed class GoogleCloudAssetStore : IAssetStore, IInitializable
{
private static readonly UploadObjectOptions IfNotExists = new UploadObjectOptions { IfGenerationMatch = 0 };
private static readonly CopyObjectOptions IfNotExistsCopy = new CopyObjectOptions { IfGenerationMatch = 0 };
private readonly string bucketName;
private StorageClient storageClient;
@ -49,29 +51,21 @@ namespace Squidex.Infrastructure.Assets
return $"https://storage.cloud.google.com/{bucketName}/{objectName}";
}
public Task UploadAsync(string name, Stream stream, CancellationToken ct = default(CancellationToken))
{
return storageClient.UploadObjectAsync(bucketName, name, "application/octet-stream", stream, cancellationToken: ct);
}
public async Task UploadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken))
{
var objectName = GetObjectName(id, version, suffix);
await storageClient.UploadObjectAsync(bucketName, objectName, "application/octet-stream", stream, cancellationToken: ct);
}
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))
{
var objectName = GetObjectName(id, version, suffix);
try
{
await storageClient.CopyObjectAsync(bucketName, name, bucketName, objectName, cancellationToken: ct);
await storageClient.CopyObjectAsync(bucketName, sourceFileName, bucketName, objectName, IfNotExistsCopy, ct);
}
catch (GoogleApiException ex) when (ex.HttpStatusCode == HttpStatusCode.NotFound)
{
throw new AssetNotFoundException(name, ex);
throw new AssetNotFoundException(sourceFileName, ex);
}
catch (GoogleApiException ex) when (ex.HttpStatusCode == HttpStatusCode.PreconditionFailed)
{
throw new AssetAlreadyExistsException(objectName);
}
}
@ -89,33 +83,47 @@ namespace Squidex.Infrastructure.Assets
}
}
public async Task DeleteAsync(string name)
public Task UploadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken))
{
return UploadCoreAsync(GetObjectName(id, version, suffix), stream, ct);
}
public Task UploadAsync(string fileName, Stream stream, CancellationToken ct = default(CancellationToken))
{
return UploadCoreAsync(fileName, stream, ct);
}
public Task DeleteAsync(string id, long version, string suffix)
{
return DeleteCoreAsync(GetObjectName(id, version, suffix));
}
public Task DeleteAsync(string fileName)
{
return DeleteCoreAsync(fileName);
}
private async Task UploadCoreAsync(string objectName, Stream stream, CancellationToken ct)
{
try
{
await storageClient.DeleteObjectAsync(bucketName, name);
await storageClient.UploadObjectAsync(bucketName, objectName, "application/octet-stream", stream, IfNotExists, ct);
}
catch (GoogleApiException ex)
catch (GoogleApiException ex) when (ex.HttpStatusCode == HttpStatusCode.PreconditionFailed)
{
if (ex.HttpStatusCode != HttpStatusCode.NotFound)
{
throw;
}
throw new AssetAlreadyExistsException(objectName);
}
}
public async Task DeleteAsync(string id, long version, string suffix)
private async Task DeleteCoreAsync(string objectName)
{
try
{
await storageClient.DeleteObjectAsync(bucketName, GetObjectName(id, version, suffix));
await storageClient.DeleteObjectAsync(bucketName, objectName);
}
catch (GoogleApiException ex)
catch (GoogleApiException ex) when (ex.HttpStatusCode == HttpStatusCode.NotFound)
{
if (ex.HttpStatusCode != HttpStatusCode.NotFound)
{
throw;
}
return;
}
}

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

@ -78,19 +78,14 @@ namespace Squidex.Infrastructure.Assets
}
}
public Task UploadAsync(string fileName, Stream stream, CancellationToken ct = default(CancellationToken))
{
return UploadFileCoreAsync(fileName, stream, ct);
}
public Task UploadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken))
{
return UploadFileCoreAsync(GetFileName(id, version, suffix), stream, ct);
}
public Task DeleteAsync(string fileName)
public Task UploadAsync(string fileName, Stream stream, CancellationToken ct = default(CancellationToken))
{
return DeleteCoreAsync(fileName);
return UploadFileCoreAsync(fileName, stream, ct);
}
public Task DeleteAsync(string id, long version, string suffix)
@ -98,6 +93,11 @@ namespace Squidex.Infrastructure.Assets
return DeleteCoreAsync(GetFileName(id, version, suffix));
}
public Task DeleteAsync(string fileName)
{
return DeleteCoreAsync(fileName);
}
private async Task DeleteCoreAsync(string id)
{
try

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

@ -98,14 +98,14 @@ namespace Squidex.Infrastructure.Assets
}
}
public Task UploadAsync(string fileName, Stream stream, CancellationToken ct = default(CancellationToken))
public Task UploadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken))
{
return UploadCoreAsync(GetFile(fileName), stream, ct);
return UploadCoreAsync(GetFile(id, version, suffix), stream, ct);
}
public Task UploadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken))
public Task UploadAsync(string fileName, Stream stream, CancellationToken ct = default(CancellationToken))
{
return UploadCoreAsync(GetFile(id, version, suffix), stream, ct);
return UploadCoreAsync(GetFile(fileName), stream, ct);
}
public Task DeleteAsync(string id, long version, string suffix)

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

@ -31,6 +31,11 @@ namespace Squidex.Infrastructure.Assets
get { return sut.Value; }
}
protected string AssetId
{
get { return assetId; }
}
public abstract T CreateStore();
public abstract void Dispose();

7
tests/Squidex.Infrastructure.Tests/Assets/AzureBlobAssetStoreTests.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Xunit;
#pragma warning disable xUnit1000 // Test classes must be public
@ -26,11 +25,9 @@ namespace Squidex.Infrastructure.Assets
[Fact]
public void Should_calculate_source_url()
{
Sut.Initialize();
var url = Sut.GenerateSourceUrl(AssetId, 1, null);
var id = Guid.NewGuid().ToString();
Assert.Equal($"http://127.0.0.1:10000/squidex-test-container/{id}_1", Sut.GenerateSourceUrl(id, 1, null));
Assert.Equal($"http://127.0.0.1:10000/squidex-test-container/{AssetId}_1", url);
}
}
}

8
tests/Squidex.Infrastructure.Tests/Assets/FolderAssetStoreTests.cs

@ -39,19 +39,15 @@ namespace Squidex.Infrastructure.Assets
[Fact]
public void Should_create_directory_when_connecting()
{
Sut.Initialize();
Assert.True(Directory.Exists(testFolder));
}
[Fact]
public void Should_calculate_source_url()
{
Sut.Initialize();
var id = Guid.NewGuid().ToString();
var url = Sut.GenerateSourceUrl(AssetId, 1, null);
Assert.Equal(Path.Combine(testFolder, $"{id}_1"), Sut.GenerateSourceUrl(id, 1, null));
Assert.Equal(Path.Combine(testFolder, $"{AssetId}_1"), url);
}
private static string CreateInvalidPath()

7
tests/Squidex.Infrastructure.Tests/Assets/GoogleCloudAssetStoreTests.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Xunit;
#pragma warning disable xUnit1000 // Test classes must be public
@ -26,11 +25,9 @@ namespace Squidex.Infrastructure.Assets
[Fact]
public void Should_calculate_source_url()
{
Sut.Initialize();
var url = Sut.GenerateSourceUrl(AssetId, 1, null);
var id = Guid.NewGuid().ToString();
Assert.Equal($"https://storage.cloud.google.com/squidex-test/{id}_1", Sut.GenerateSourceUrl(id, 1, null));
Assert.Equal($"https://storage.cloud.google.com/squidex-test/{AssetId}_1", url);
}
}
}

5
tests/Squidex.Infrastructure.Tests/Assets/MongoGridFsAssetStoreTests.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using MongoDB.Driver;
using MongoDB.Driver.GridFS;
using Xunit;
@ -43,9 +42,7 @@ namespace Squidex.Infrastructure.Assets
[Fact]
public void Should_not_calculate_source_url()
{
Sut.Initialize();
Assert.Equal("UNSUPPORTED", Sut.GenerateSourceUrl(Guid.NewGuid().ToString(), 1, null));
Assert.Equal("UNSUPPORTED", Sut.GenerateSourceUrl(AssetId, 1, null));
}
}
}
Loading…
Cancel
Save