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(); 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 targetName = GetObjectName(id, version, suffix);
var blobRef = blobContainer.GetBlobReference(blobName); var targetBlob = blobContainer.GetBlobReference(targetName);
var tempBlob = blobContainer.GetBlockBlobReference(name); var sourceBlob = blobContainer.GetBlockBlobReference(sourceFileName);
try 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(); ct.ThrowIfCancellationRequested();
await Task.Delay(50); 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) 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)) public async Task DownloadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken))
{ {
var blobName = GetObjectName(id, version, suffix); var blob = blobContainer.GetBlockBlobReference(GetObjectName(id, version, suffix));
var blobRef = blobContainer.GetBlockBlobReference(blobName);
try 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) 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); return UploadCoreAsync(GetObjectName(id, version, suffix), stream, ct);
var blobRef = blobContainer.GetBlockBlobReference(blobName); }
blobRef.Metadata[AssetVersion] = version.ToString();
blobRef.Metadata[AssetId] = id;
await blobRef.UploadFromStreamAsync(stream, null, null, null, ct); public Task UploadAsync(string fileName, Stream stream, CancellationToken ct = default(CancellationToken))
await blobRef.SetMetadataAsync(); {
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) 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 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 readonly string bucketName;
private StorageClient storageClient; private StorageClient storageClient;
@ -49,29 +51,21 @@ namespace Squidex.Infrastructure.Assets
return $"https://storage.cloud.google.com/{bucketName}/{objectName}"; return $"https://storage.cloud.google.com/{bucketName}/{objectName}";
} }
public Task UploadAsync(string name, Stream stream, CancellationToken ct = default(CancellationToken)) public async Task CopyAsync(string sourceFileName, string id, long version, string suffix, 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))
{ {
var objectName = GetObjectName(id, version, suffix); var objectName = GetObjectName(id, version, suffix);
try 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) 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 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 new AssetAlreadyExistsException(objectName);
{
throw;
}
} }
} }
public async Task DeleteAsync(string id, long version, string suffix) private async Task DeleteCoreAsync(string objectName)
{ {
try 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) return;
{
throw;
}
} }
} }

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)) public Task UploadAsync(string id, long version, string suffix, Stream stream, CancellationToken ct = default(CancellationToken))
{ {
return UploadFileCoreAsync(GetFileName(id, version, suffix), stream, ct); 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) public Task DeleteAsync(string id, long version, string suffix)
@ -98,6 +93,11 @@ namespace Squidex.Infrastructure.Assets
return DeleteCoreAsync(GetFileName(id, version, suffix)); return DeleteCoreAsync(GetFileName(id, version, suffix));
} }
public Task DeleteAsync(string fileName)
{
return DeleteCoreAsync(fileName);
}
private async Task DeleteCoreAsync(string id) private async Task DeleteCoreAsync(string id)
{ {
try 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) 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; } get { return sut.Value; }
} }
protected string AssetId
{
get { return assetId; }
}
public abstract T CreateStore(); public abstract T CreateStore();
public abstract void Dispose(); public abstract void Dispose();

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

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

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

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

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

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

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

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