Browse Source

Merge remote-tracking branch 'Squidex/master' into feature-azure-storage

pull/76/head
pushrbx 9 years ago
parent
commit
8a05982ca8
  1. 43
      src/Squidex.Domain.Apps.Write/Assets/AssetCommandHandler.cs
  2. 4
      src/Squidex.Infrastructure.GetEventStore/CQRS/Events/Formatter.cs
  3. 4
      src/Squidex.Infrastructure.GetEventStore/CQRS/Events/GetEventStore.cs
  4. 3
      src/Squidex.Infrastructure.GetEventStore/CQRS/Events/GetEventStoreSubscription.cs
  5. 1
      src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj
  6. 62
      src/Squidex.Infrastructure.GoogleCloud/Assets/GoogleCloudAssetStore.cs
  7. 1
      src/Squidex.Infrastructure.GoogleCloud/Squidex.Infrastructure.GoogleCloud.csproj
  8. 2
      src/Squidex.Infrastructure.MongoDb/CQRS/Events/MongoEvent.cs
  9. 2
      src/Squidex.Infrastructure.MongoDb/CQRS/Events/MongoEventCommit.cs
  10. 3
      src/Squidex.Infrastructure.MongoDb/CQRS/Events/MongoEventConsumerInfo.cs
  11. 4
      src/Squidex.Infrastructure.MongoDb/CQRS/Events/MongoEventConsumerInfoRepository.cs
  12. 8
      src/Squidex.Infrastructure.MongoDb/CQRS/Events/MongoEventStore.cs
  13. 3
      src/Squidex.Infrastructure.MongoDb/CQRS/Events/PollingSubscription.cs
  14. 2
      src/Squidex.Infrastructure.MongoDb/CQRS/Events/StreamPosition.cs
  15. 0
      src/Squidex.Infrastructure.MongoDb/MongoDb/FieldDefinitionBuilder.cs
  16. 0
      src/Squidex.Infrastructure.MongoDb/MongoDb/IMongoEntity.cs
  17. 0
      src/Squidex.Infrastructure.MongoDb/MongoDb/InstantSerializer.cs
  18. 0
      src/Squidex.Infrastructure.MongoDb/MongoDb/MongoEntity.cs
  19. 0
      src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs
  20. 0
      src/Squidex.Infrastructure.MongoDb/MongoDb/MongoRepositoryBase.cs
  21. 0
      src/Squidex.Infrastructure.MongoDb/MongoDb/RefTokenSerializer.cs
  22. 1
      src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj
  23. 2
      src/Squidex.Infrastructure.MongoDb/UsageTracking/MongoUsage.cs
  24. 4
      src/Squidex.Infrastructure.MongoDb/UsageTracking/MongoUsageStore.cs
  25. 3
      src/Squidex.Infrastructure.RabbitMq/CQRS/Events/RabbitMqEventConsumer.cs
  26. 1
      src/Squidex.Infrastructure.RabbitMq/Squidex.Infrastructure.RabbitMq.csproj
  27. 2
      src/Squidex.Infrastructure.Redis/RedisPubSub.cs
  28. 2
      src/Squidex.Infrastructure.Redis/RedisSubscription.cs
  29. 1
      src/Squidex.Infrastructure.Redis/Squidex.Infrastructure.Redis.csproj
  30. 74
      src/Squidex.Infrastructure/Assets/FolderAssetStore.cs
  31. 6
      src/Squidex.Infrastructure/Assets/IAssetStore.cs
  32. 8
      src/Squidex.Infrastructure/CQRS/Commands/AggregateHandler.cs
  33. 4
      src/Squidex.Infrastructure/CQRS/Commands/IAggregateHandler.cs
  34. 1
      src/Squidex/Config/Domain/AssetStoreModule.cs
  35. 1
      src/Squidex/Config/Domain/EventPublishersModule.cs
  36. 2
      src/Squidex/Config/Domain/EventStoreModule.cs
  37. 1
      src/Squidex/Config/Domain/PubSubModule.cs
  38. 2
      src/Squidex/Config/Domain/StoreMongoDbModule.cs
  39. 8
      src/Squidex/app/shared/interceptors/auth.interceptor.ts
  40. 1
      tests/Benchmarks/Tests/AppendToEventStore.cs
  41. 1
      tests/Benchmarks/Tests/AppendToEventStoreWithManyWriters.cs
  42. 1
      tests/Benchmarks/Tests/HandleEvents.cs
  43. 1
      tests/Benchmarks/Tests/HandleEventsWithManyWriters.cs
  44. 4
      tests/Squidex.Domain.Apps.Read.Tests/Schemas/ODataQueryTests.cs
  45. 25
      tests/Squidex.Domain.Apps.Write.Tests/Assets/AssetCommandHandlerTests.cs
  46. 16
      tests/Squidex.Domain.Apps.Write.Tests/TestHelpers/HandlerTestBase.cs
  47. 110
      tests/Squidex.Infrastructure.Tests/Assets/AssetStoreTestsBase.cs
  48. 43
      tests/Squidex.Infrastructure.Tests/Assets/FolderAssetStoreTests.cs
  49. 22
      tests/Squidex.Infrastructure.Tests/Assets/GoogleCloudAssetStoreTests.cs
  50. 2
      tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj

43
src/Squidex.Domain.Apps.Write/Assets/AssetCommandHandler.cs

@ -38,38 +38,55 @@ namespace Squidex.Domain.Apps.Write.Assets
protected async Task On(CreateAsset command, CommandContext context)
{
await handler.CreateAsync<AssetDomainObject>(context, async c =>
command.ImageInfo = await assetThumbnailGenerator.GetImageInfoAsync(command.File.OpenRead());
try
{
command.ImageInfo = await assetThumbnailGenerator.GetImageInfoAsync(command.File.OpenRead());
var asset = await handler.CreateAsync<AssetDomainObject>(context, async a =>
{
a.Create(command);
c.Create(command);
await assetStore.UploadTemporaryAsync(context.ContextId.ToString(), command.File.OpenRead());
await assetStore.UploadAsync(c.Id.ToString(), c.FileVersion, null, command.File.OpenRead());
context.Succeed(EntityCreatedResult.Create(a.Id, a.Version));
});
context.Succeed(EntityCreatedResult.Create(c.Id, c.Version));
});
await assetStore.CopyTemporaryAsync(context.ContextId.ToString(), asset.Id.ToString(), asset.FileVersion, null);
}
finally
{
await assetStore.DeleteTemporaryAsync(context.ContextId.ToString());
}
}
protected async Task On(UpdateAsset command, CommandContext context)
{
await handler.UpdateAsync<AssetDomainObject>(context, async c =>
command.ImageInfo = await assetThumbnailGenerator.GetImageInfoAsync(command.File.OpenRead());
try
{
command.ImageInfo = await assetThumbnailGenerator.GetImageInfoAsync(command.File.OpenRead());
var asset = await handler.UpdateAsync<AssetDomainObject>(context, async a =>
{
a.Update(command);
c.Update(command);
await assetStore.UploadTemporaryAsync(context.ContextId.ToString(), command.File.OpenRead());
});
await assetStore.UploadAsync(c.Id.ToString(), c.FileVersion, null, command.File.OpenRead());
});
await assetStore.CopyTemporaryAsync(context.ContextId.ToString(), asset.Id.ToString(), asset.FileVersion, null);
}
finally
{
await assetStore.DeleteTemporaryAsync(context.ContextId.ToString());
}
}
protected Task On(RenameAsset command, CommandContext context)
{
return handler.UpdateAsync<AssetDomainObject>(context, c => c.Rename(command));
return handler.UpdateAsync<AssetDomainObject>(context, a => a.Rename(command));
}
protected Task On(DeleteAsset command, CommandContext context)
{
return handler.UpdateAsync<AssetDomainObject>(context, c => c.Delete(command));
return handler.UpdateAsync<AssetDomainObject>(context, a => a.Delete(command));
}
public Task<bool> HandleAsync(CommandContext context)

4
src/Squidex.Infrastructure.GetEventStore/Formatter.cs → src/Squidex.Infrastructure.GetEventStore/CQRS/Events/Formatter.cs

@ -8,11 +8,9 @@
using System.Text;
using EventStore.ClientAPI;
using Squidex.Infrastructure.CQRS.Events;
using EventData = Squidex.Infrastructure.CQRS.Events.EventData;
using EventStoreData = EventStore.ClientAPI.EventData;
namespace Squidex.Infrastructure.GetEventStore
namespace Squidex.Infrastructure.CQRS.Events
{
public static class Formatter
{

4
src/Squidex.Infrastructure.GetEventStore/GetEventStore.cs → src/Squidex.Infrastructure.GetEventStore/CQRS/Events/GetEventStore.cs

@ -11,13 +11,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EventStore.ClientAPI;
using Squidex.Infrastructure.CQRS.Events;
using EventData = Squidex.Infrastructure.CQRS.Events.EventData;
// ReSharper disable ConvertIfStatementToSwitchStatement
// ReSharper disable InvertIf
namespace Squidex.Infrastructure.GetEventStore
namespace Squidex.Infrastructure.CQRS.Events
{
public sealed class GetEventStore : IEventStore, IExternalSystem
{

3
src/Squidex.Infrastructure.GetEventStore/GetEventStoreSubscription.cs → src/Squidex.Infrastructure.GetEventStore/CQRS/Events/GetEventStoreSubscription.cs

@ -15,9 +15,8 @@ using System.Threading.Tasks;
using EventStore.ClientAPI;
using EventStore.ClientAPI.Exceptions;
using EventStore.ClientAPI.Projections;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Infrastructure.GetEventStore
namespace Squidex.Infrastructure.CQRS.Events
{
internal sealed class EventStoreSubscription : DisposableObjectBase, IEventSubscription
{

1
src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<RootNamespace>Squidex.Infrastructure</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>

62
src/Squidex.Infrastructure.GoogleCloud/GoogleCloudAssetStore.cs → src/Squidex.Infrastructure.GoogleCloud/Assets/GoogleCloudAssetStore.cs

@ -8,13 +8,13 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Google;
using Google.Cloud.Storage.V1;
using Squidex.Infrastructure.Assets;
namespace Squidex.Infrastructure.GoogleCloud
namespace Squidex.Infrastructure.Assets
{
public sealed class GoogleCloudAssetStore : IAssetStore, IExternalSystem
{
@ -42,6 +42,36 @@ namespace Squidex.Infrastructure.GoogleCloud
}
}
public Task UploadTemporaryAsync(string name, Stream stream)
{
return storageClient.UploadObjectAsync(bucketName, name, "application/octet-stream", stream);
}
public async Task UploadAsync(string id, long version, string suffix, Stream stream)
{
var objectName = GetObjectName(id, version, suffix);
await storageClient.UploadObjectAsync(bucketName, objectName, "application/octet-stream", stream);
}
public async Task CopyTemporaryAsync(string name, string id, long version, string suffix)
{
var objectName = GetObjectName(id, version, suffix);
try
{
await storageClient.CopyObjectAsync(bucketName, name, bucketName, objectName);
}
catch (GoogleApiException ex)
{
if (ex.HttpStatusCode == HttpStatusCode.NotFound)
{
throw new AssetNotFoundException($"Asset {name} not found.", ex);
}
throw;
}
}
public async Task DownloadAsync(string id, long version, string suffix, Stream stream)
{
var objectName = GetObjectName(id, version, suffix);
@ -60,11 +90,19 @@ namespace Squidex.Infrastructure.GoogleCloud
}
}
public async Task UploadAsync(string id, long version, string suffix, Stream stream)
public async Task DeleteTemporaryAsync(string name)
{
var objectName = GetObjectName(id, version, suffix);
await storageClient.UploadObjectAsync(bucketName, objectName, "application/octet-stream", stream);
try
{
await storageClient.DeleteObjectAsync(bucketName, name);
}
catch (GoogleApiException ex)
{
if (ex.HttpStatusCode != HttpStatusCode.NotFound)
{
throw;
}
}
}
private string GetObjectName(string id, long version, string suffix)
@ -76,14 +114,14 @@ namespace Squidex.Infrastructure.GoogleCloud
throw new InvalidOperationException("No connection established yet.");
}
var name = $"{id}_{version}";
if (!string.IsNullOrWhiteSpace(suffix))
{
name += "_" + suffix;
}
var name = GetFileName(id, version, suffix);
return name;
}
private static string GetFileName(string id, long version, string suffix)
{
return string.Join("_", new[] { id, version.ToString(), suffix }.Where(x => !string.IsNullOrWhiteSpace(x)));
}
}
}

1
src/Squidex.Infrastructure.GoogleCloud/Squidex.Infrastructure.GoogleCloud.csproj

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<RootNamespace>Squidex.Infrastructure</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>

2
src/Squidex.Infrastructure.MongoDb/EventStore/MongoEvent.cs → src/Squidex.Infrastructure.MongoDb/CQRS/Events/MongoEvent.cs

@ -9,7 +9,7 @@
using System;
using MongoDB.Bson.Serialization.Attributes;
namespace Squidex.Infrastructure.MongoDb.EventStore
namespace Squidex.Infrastructure.CQRS.Events
{
public class MongoEvent
{

2
src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventCommit.cs → src/Squidex.Infrastructure.MongoDb/CQRS/Events/MongoEventCommit.cs

@ -10,7 +10,7 @@ using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Squidex.Infrastructure.MongoDb.EventStore
namespace Squidex.Infrastructure.CQRS.Events
{
public sealed class MongoEventCommit
{

3
src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventConsumerInfo.cs → src/Squidex.Infrastructure.MongoDb/CQRS/Events/MongoEventConsumerInfo.cs

@ -8,9 +8,8 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Squidex.Infrastructure.CQRS.Events;
namespace Squidex.Infrastructure.MongoDb.EventStore
namespace Squidex.Infrastructure.CQRS.Events
{
public sealed class MongoEventConsumerInfo : IEventConsumerInfo
{

4
src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventConsumerInfoRepository.cs → src/Squidex.Infrastructure.MongoDb/CQRS/Events/MongoEventConsumerInfoRepository.cs

@ -11,12 +11,12 @@ using System.Linq;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.MongoDb;
// ReSharper disable ConvertIfStatementToReturnStatement
// ReSharper disable RedundantIfElseBlock
namespace Squidex.Infrastructure.MongoDb.EventStore
namespace Squidex.Infrastructure.CQRS.Events
{
public sealed class MongoEventConsumerInfoRepository : MongoRepositoryBase<MongoEventConsumerInfo>, IEventConsumerInfoRepository
{

8
src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventStore.cs → src/Squidex.Infrastructure.MongoDb/CQRS/Events/MongoEventStore.cs

@ -6,22 +6,22 @@
// All rights reserved.
// ==========================================================================
using MongoDB.Bson;
using MongoDB.Driver;
using Squidex.Infrastructure.CQRS.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Tasks;
// ReSharper disable RedundantIfElseBlock
// ReSharper disable InvertIf
// ReSharper disable ConvertIfStatementToConditionalTernaryExpression
namespace Squidex.Infrastructure.MongoDb.EventStore
namespace Squidex.Infrastructure.CQRS.Events
{
public class MongoEventStore : MongoRepositoryBase<MongoEventCommit>, IEventStore
{

3
src/Squidex.Infrastructure.MongoDb/EventStore/PollingSubscription.cs → src/Squidex.Infrastructure.MongoDb/CQRS/Events/PollingSubscription.cs

@ -8,13 +8,12 @@
using System;
using System.Threading.Tasks;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Tasks;
using Squidex.Infrastructure.Timers;
// ReSharper disable InvertIf
namespace Squidex.Infrastructure.MongoDb.EventStore
namespace Squidex.Infrastructure.CQRS.Events
{
public sealed class PollingSubscription : DisposableObjectBase, IEventSubscription
{

2
src/Squidex.Infrastructure.MongoDb/EventStore/StreamPosition.cs → src/Squidex.Infrastructure.MongoDb/CQRS/Events/StreamPosition.cs

@ -10,7 +10,7 @@
using MongoDB.Bson;
namespace Squidex.Infrastructure.MongoDb.EventStore
namespace Squidex.Infrastructure.CQRS.Events
{
public sealed class StreamPosition
{

0
src/Squidex.Infrastructure.MongoDb/FieldDefinitionBuilder.cs → src/Squidex.Infrastructure.MongoDb/MongoDb/FieldDefinitionBuilder.cs

0
src/Squidex.Infrastructure.MongoDb/IMongoEntity.cs → src/Squidex.Infrastructure.MongoDb/MongoDb/IMongoEntity.cs

0
src/Squidex.Infrastructure.MongoDb/InstantSerializer.cs → src/Squidex.Infrastructure.MongoDb/MongoDb/InstantSerializer.cs

0
src/Squidex.Infrastructure.MongoDb/MongoEntity.cs → src/Squidex.Infrastructure.MongoDb/MongoDb/MongoEntity.cs

0
src/Squidex.Infrastructure.MongoDb/MongoExtensions.cs → src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs

0
src/Squidex.Infrastructure.MongoDb/MongoRepositoryBase.cs → src/Squidex.Infrastructure.MongoDb/MongoDb/MongoRepositoryBase.cs

0
src/Squidex.Infrastructure.MongoDb/RefTokenSerializer.cs → src/Squidex.Infrastructure.MongoDb/MongoDb/RefTokenSerializer.cs

1
src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<RootNamespace>Squidex.Infrastructure</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>

2
src/Squidex.Infrastructure.MongoDb/UsageTracker/MongoUsage.cs → src/Squidex.Infrastructure.MongoDb/UsageTracking/MongoUsage.cs

@ -10,7 +10,7 @@ using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Squidex.Infrastructure.MongoDb.UsageTracker
namespace Squidex.Infrastructure.UsageTracking
{
public sealed class MongoUsage
{

4
src/Squidex.Infrastructure.MongoDb/UsageTracker/MongoUsageStore.cs → src/Squidex.Infrastructure.MongoDb/UsageTracking/MongoUsageStore.cs

@ -11,9 +11,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using Squidex.Infrastructure.UsageTracking;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Infrastructure.MongoDb.UsageTracker
namespace Squidex.Infrastructure.UsageTracking
{
public sealed class MongoUsageStore : MongoRepositoryBase<MongoUsage>, IUsageStore
{

3
src/Squidex.Infrastructure.RabbitMq/RabbitMqEventConsumer.cs → src/Squidex.Infrastructure.RabbitMq/CQRS/Events/RabbitMqEventConsumer.cs

@ -11,12 +11,11 @@ using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using RabbitMQ.Client;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Tasks;
// ReSharper disable InvertIf
namespace Squidex.Infrastructure.RabbitMq
namespace Squidex.Infrastructure.CQRS.Events
{
public sealed class RabbitMqEventConsumer : DisposableObjectBase, IExternalSystem, IEventConsumer
{

1
src/Squidex.Infrastructure.RabbitMq/Squidex.Infrastructure.RabbitMq.csproj

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<RootNamespace>Squidex.Infrastructure</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>

2
src/Squidex.Infrastructure.Redis/RedisPubSub.cs

@ -11,7 +11,7 @@ using System.Collections.Concurrent;
using Squidex.Infrastructure.Log;
using StackExchange.Redis;
namespace Squidex.Infrastructure.Redis
namespace Squidex.Infrastructure
{
public class RedisPubSub : IPubSub, IExternalSystem
{

2
src/Squidex.Infrastructure.Redis/RedisSubscription.cs

@ -14,7 +14,7 @@ using StackExchange.Redis;
// ReSharper disable InvertIf
namespace Squidex.Infrastructure.Redis
namespace Squidex.Infrastructure
{
internal sealed class RedisSubscription
{

1
src/Squidex.Infrastructure.Redis/Squidex.Infrastructure.Redis.csproj

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<RootNamespace>Squidex.Infrastructure</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>

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

@ -7,8 +7,10 @@
// ==========================================================================
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Infrastructure.Assets
{
@ -49,6 +51,26 @@ namespace Squidex.Infrastructure.Assets
}
}
public async Task UploadTemporaryAsync(string name, Stream stream)
{
var file = GetFile(name);
using (var fileStream = file.OpenWrite())
{
await stream.CopyToAsync(fileStream);
}
}
public async Task UploadAsync(string id, long version, string suffix, Stream stream)
{
var file = GetFile(id, version, suffix);
using (var fileStream = file.OpenWrite())
{
await stream.CopyToAsync(fileStream);
}
}
public async Task DownloadAsync(string id, long version, string suffix, Stream stream)
{
var file = GetFile(id, version, suffix);
@ -66,28 +88,60 @@ namespace Squidex.Infrastructure.Assets
}
}
public async Task UploadAsync(string id, long version, string suffix, Stream stream)
public Task CopyTemporaryAsync(string name, string id, long version, string suffix)
{
var file = GetFile(id, version, suffix);
try
{
var file = GetFile(name);
using (var fileStream = file.OpenWrite())
file.CopyTo(GetPath(id, version, suffix));
return TaskHelper.Done;
}
catch (FileNotFoundException ex)
{
await stream.CopyToAsync(fileStream);
throw new AssetNotFoundException($"Asset {name} not found.", ex);
}
}
private FileInfo GetFile(string id, long version, string suffix)
public Task DeleteTemporaryAsync(string name)
{
Guard.NotNullOrEmpty(id, nameof(id));
try
{
var file = GetFile(name);
var path = Path.Combine(directory.FullName, $"{id}_{version}");
file.Delete();
if (!string.IsNullOrWhiteSpace(suffix))
return TaskHelper.Done;
}
catch (FileNotFoundException ex)
{
path += "_" + suffix;
throw new AssetNotFoundException($"Asset {name} not found.", ex);
}
}
return new FileInfo(path);
private FileInfo GetFile(string id, long version, string suffix)
{
Guard.NotNullOrEmpty(id, nameof(id));
return GetFile(GetPath(id, version, suffix));
}
private FileInfo GetFile(string name)
{
Guard.NotNullOrEmpty(name, nameof(name));
return new FileInfo(GetPath(name));
}
private string GetPath(string name)
{
return Path.Combine(directory.FullName, name);
}
private string GetPath(string id, long version, string suffix)
{
return Path.Combine(directory.FullName, string.Join("_", new[] { id, version.ToString(), suffix }.Where(x => !string.IsNullOrWhiteSpace(x))));
}
}
}

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

@ -13,8 +13,14 @@ namespace Squidex.Infrastructure.Assets
{
public interface IAssetStore
{
Task CopyTemporaryAsync(string name, string id, long version, string suffix);
Task DownloadAsync(string id, long version, string suffix, Stream stream);
Task UploadTemporaryAsync(string name, Stream stream);
Task UploadAsync(string id, long version, string suffix, Stream stream);
Task DeleteTemporaryAsync(string name);
}
}

8
src/Squidex.Infrastructure/CQRS/Commands/AggregateHandler.cs

@ -38,7 +38,7 @@ namespace Squidex.Infrastructure.CQRS.Commands
this.domainObjectRepository = domainObjectRepository;
}
public async Task CreateAsync<T>(CommandContext context, Func<T, Task> creator) where T : class, IAggregate
public async Task<T> CreateAsync<T>(CommandContext context, Func<T, Task> creator) where T : class, IAggregate
{
Guard.NotNull(creator, nameof(creator));
Guard.NotNull(context, nameof(context));
@ -54,9 +54,11 @@ namespace Squidex.Infrastructure.CQRS.Commands
{
context.Succeed(new EntityCreatedResult<Guid>(aggregate.Id, aggregate.Version));
}
return aggregate;
}
public async Task UpdateAsync<T>(CommandContext context, Func<T, Task> updater) where T : class, IAggregate
public async Task<T> UpdateAsync<T>(CommandContext context, Func<T, Task> updater) where T : class, IAggregate
{
Guard.NotNull(updater, nameof(updater));
Guard.NotNull(context, nameof(context));
@ -72,6 +74,8 @@ namespace Squidex.Infrastructure.CQRS.Commands
{
context.Succeed(new EntitySavedResult(aggregate.Version));
}
return aggregate;
}
private static IAggregateCommand GetCommand(CommandContext context)

4
src/Squidex.Infrastructure/CQRS/Commands/IAggregateHandler.cs

@ -13,8 +13,8 @@ namespace Squidex.Infrastructure.CQRS.Commands
{
public interface IAggregateHandler
{
Task CreateAsync<T>(CommandContext context, Func<T, Task> creator) where T : class, IAggregate;
Task<T> CreateAsync<T>(CommandContext context, Func<T, Task> creator) where T : class, IAggregate;
Task UpdateAsync<T>(CommandContext context, Func<T, Task> updater) where T : class, IAggregate;
Task<T> UpdateAsync<T>(CommandContext context, Func<T, Task> updater) where T : class, IAggregate;
}
}

1
src/Squidex/Config/Domain/AssetStoreModule.cs

@ -11,7 +11,6 @@ using Autofac;
using Microsoft.Extensions.Configuration;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Assets;
using Squidex.Infrastructure.GoogleCloud;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Azure.Storage;

1
src/Squidex/Config/Domain/EventPublishersModule.cs

@ -12,7 +12,6 @@ using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.RabbitMq;
// ReSharper disable InvertIf

2
src/Squidex/Config/Domain/EventStoreModule.cs

@ -14,8 +14,6 @@ using Microsoft.Extensions.Configuration;
using MongoDB.Driver;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.GetEventStore;
using Squidex.Infrastructure.MongoDb.EventStore;
namespace Squidex.Config.Domain
{

1
src/Squidex/Config/Domain/PubSubModule.cs

@ -11,7 +11,6 @@ using Autofac;
using Autofac.Core;
using Microsoft.Extensions.Configuration;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Redis;
using StackExchange.Redis;
namespace Squidex.Config.Domain

2
src/Squidex/Config/Domain/StoreMongoDbModule.cs

@ -30,8 +30,6 @@ using Squidex.Domain.Users.MongoDb;
using Squidex.Domain.Users.MongoDb.Infrastructure;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.MongoDb.EventStore;
using Squidex.Infrastructure.MongoDb.UsageTracker;
using Squidex.Infrastructure.UsageTracking;
using Squidex.Shared.Users;

8
src/Squidex/app/shared/interceptors/auth.interceptor.ts

@ -49,13 +49,13 @@ export class AuthInterceptor implements HttpInterceptor {
} else if (error.status === 404 && (!user || user.isExpired)) {
this.authService.logoutRedirect();
return Observable.empty<Response>();
}else if (error.status === 401 || error.status === 403) {
return Observable.empty<HttpEvent<any>>();
} else if (error.status === 401 || error.status === 403) {
this.authService.logoutRedirect();
return Observable.empty<Response>();
return Observable.empty<HttpEvent<any>>();
}
return Observable.throw(error);
});
}
}
}

1
tests/Benchmarks/Tests/AppendToEventStore.cs

@ -11,7 +11,6 @@ using Benchmarks.Utils;
using MongoDB.Driver;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.MongoDb.EventStore;
namespace Benchmarks.Tests
{

1
tests/Benchmarks/Tests/AppendToEventStoreWithManyWriters.cs

@ -12,7 +12,6 @@ using Benchmarks.Utils;
using MongoDB.Driver;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.MongoDb.EventStore;
namespace Benchmarks.Tests
{

1
tests/Benchmarks/Tests/HandleEvents.cs

@ -15,7 +15,6 @@ using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.MongoDb.EventStore;
// ReSharper disable InvertIf

1
tests/Benchmarks/Tests/HandleEventsWithManyWriters.cs

@ -16,7 +16,6 @@ using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.MongoDb.EventStore;
// ReSharper disable InvertIf

4
tests/Squidex.Domain.Apps.Read.Tests/Schemas/ODataQueryTests.cs

@ -18,15 +18,15 @@ using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Contents.Edm;
using Squidex.Domain.Apps.Read.MongoDb.Contents;
using Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Xunit;
// ReSharper disable SpecifyACultureInStringConversionExplicitly
namespace Squidex.Domain.Apps.Read.MongoDb.Contents
namespace Squidex.Domain.Apps.Read.Schemas
{
public class ODataQueryTests
{

25
tests/Squidex.Domain.Apps.Write.Tests/Assets/AssetCommandHandlerTests.cs

@ -17,6 +17,7 @@ using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Tasks;
using Xunit;
// ReSharper disable ImplicitlyCapturedClosure
// ReSharper disable ConvertToConstant.Local
namespace Squidex.Domain.Apps.Write.Assets
@ -44,11 +45,11 @@ namespace Squidex.Domain.Apps.Write.Assets
[Fact]
public async Task Create_should_create_asset()
{
SetupStore(0);
SetupImageInfo();
var context = CreateContextForCommand(new CreateAsset { AssetId = assetId, File = file });
SetupStore(0, context.ContextId);
SetupImageInfo();
await TestCreate(asset, async _ =>
{
await sut.HandleAsync(context);
@ -63,13 +64,13 @@ namespace Squidex.Domain.Apps.Write.Assets
[Fact]
public async Task Update_should_update_domain_object()
{
SetupStore(1);
var context = CreateContextForCommand(new UpdateAsset { AssetId = assetId, File = file });
SetupStore(1, context.ContextId);
SetupImageInfo();
CreateAsset();
var context = CreateContextForCommand(new UpdateAsset { AssetId = assetId, File = file });
await TestUpdate(asset, async _ =>
{
await sut.HandleAsync(context);
@ -117,10 +118,18 @@ namespace Squidex.Domain.Apps.Write.Assets
.Verifiable();
}
private void SetupStore(long version)
private void SetupStore(long version, Guid commitId)
{
assetStore
.Setup(x => x.UploadAsync(assetId.ToString(), version, null, stream)).Returns(TaskHelper.Done)
.Setup(x => x.UploadTemporaryAsync(commitId.ToString(), stream)).Returns(TaskHelper.Done)
.Verifiable();
assetStore
.Setup(x => x.CopyTemporaryAsync(commitId.ToString(), assetId.ToString(), version, null)).Returns(TaskHelper.Done)
.Verifiable();
assetStore
.Setup(x => x.DeleteTemporaryAsync(commitId.ToString())).Returns(TaskHelper.Done)
.Verifiable();
}
}

16
tests/Squidex.Domain.Apps.Write.Tests/TestHelpers/HandlerTestBase.cs

@ -34,18 +34,26 @@ namespace Squidex.Domain.Apps.Write.TestHelpers
IsUpdated = false;
}
public Task CreateAsync<V>(CommandContext context, Func<V, Task> creator) where V : class, IAggregate
public async Task<V> CreateAsync<V>(CommandContext context, Func<V, Task> creator) where V : class, IAggregate
{
IsCreated = true;
return creator(domainObject as V);
var @do = domainObject as V;
await creator(domainObject as V);
return @do;
}
public Task UpdateAsync<V>(CommandContext context, Func<V, Task> updater) where V : class, IAggregate
public async Task<V> UpdateAsync<V>(CommandContext context, Func<V, Task> updater) where V : class, IAggregate
{
IsUpdated = true;
return updater(domainObject as V);
var @do = domainObject as V;
await updater(domainObject as V);
return @do;
}
}

110
tests/Squidex.Infrastructure.Tests/Assets/AssetStoreTestsBase.cs

@ -0,0 +1,110 @@
// ==========================================================================
// AssetStoreTestsBase.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.IO;
using System.Threading.Tasks;
using Xunit;
// ReSharper disable VirtualMemberCallInConstructor
// ReSharper disable MemberCanBeProtected.Global
namespace Squidex.Infrastructure.Assets
{
public abstract class AssetStoreTests<T> : IDisposable where T : IAssetStore
{
private readonly T sut;
protected AssetStoreTests()
{
sut = CreateStore();
}
protected T Sut
{
get { return sut; }
}
public abstract T CreateStore();
public abstract void Dispose();
[Fact]
public Task Should_throw_exception_if_asset_to_download_is_not_found()
{
((IExternalSystem)Sut).Connect();
return Assert.ThrowsAsync<AssetNotFoundException>(() => Sut.DownloadAsync(Id(), 1, "suffix", new MemoryStream()));
}
[Fact]
public Task Should_throw_exception_if_asset_to_copy_is_not_found()
{
((IExternalSystem)Sut).Connect();
return Assert.ThrowsAsync<AssetNotFoundException>(() => Sut.CopyTemporaryAsync(Id(), Id(), 1, null));
}
[Fact]
public async Task Should_read_and_write_file()
{
((IExternalSystem)Sut).Connect();
var assetId = Id();
var assetData = new MemoryStream(new byte[] { 0x1, 0x2, 0x3, 0x4 });
await Sut.UploadAsync(assetId, 1, "suffix", assetData);
var readData = new MemoryStream();
await Sut.DownloadAsync(assetId, 1, "suffix", readData);
Assert.Equal(assetData.ToArray(), readData.ToArray());
}
[Fact]
public async Task Should_commit_temporary_file()
{
((IExternalSystem)Sut).Connect();
var tempId = Id();
var assetId = Id();
var assetData = new MemoryStream(new byte[] { 0x1, 0x2, 0x3, 0x4 });
await Sut.UploadTemporaryAsync(tempId, assetData);
await Sut.CopyTemporaryAsync(tempId, assetId, 1, "suffix");
var readData = new MemoryStream();
await Sut.DownloadAsync(assetId, 1, "suffix", readData);
Assert.Equal(assetData.ToArray(), readData.ToArray());
}
[Fact]
public async Task Should_ignore_when_deleting_twice()
{
((IExternalSystem)Sut).Connect();
var tempId = Id();
var assetData = new MemoryStream(new byte[] { 0x1, 0x2, 0x3, 0x4 });
await Sut.UploadTemporaryAsync(tempId, assetData);
await Sut.DeleteTemporaryAsync(tempId);
await Sut.DeleteTemporaryAsync(tempId);
}
private static string Id()
{
return Guid.NewGuid().ToString();
}
}
}

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

@ -8,39 +8,29 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Moq;
using Squidex.Infrastructure.Log;
using Xunit;
namespace Squidex.Infrastructure.Assets
{
public class FolderAssetStoreTests : IDisposable
public class FolderAssetStoreTests : AssetStoreTests<FolderAssetStore>
{
private readonly FolderAssetStore sut;
private readonly string testFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
public FolderAssetStoreTests()
public override FolderAssetStore CreateStore()
{
sut = new FolderAssetStore(testFolder, new Mock<ISemanticLog>().Object);
return new FolderAssetStore(testFolder, new Mock<ISemanticLog>().Object);
}
public void Dispose()
public override void Dispose()
{
if (Directory.Exists(testFolder))
{
Directory.Delete(testFolder, true);
}
}
[Fact]
public void Should_create_directory_when_connecting()
{
sut.Connect();
Assert.True(Directory.Exists(testFolder));
}
[Fact]
public void Should_throw_when_creating_directory_failed()
{
@ -48,28 +38,11 @@ namespace Squidex.Infrastructure.Assets
}
[Fact]
public Task Should_throw_exception_if_asset_not_found()
{
sut.Connect();
return Assert.ThrowsAsync<AssetNotFoundException>(() => sut.DownloadAsync(Guid.NewGuid().ToString(), 1, "suffix", new MemoryStream()));
}
[Fact]
public async Task Should_read_and_write_file()
public void Should_create_directory_when_connecting()
{
sut.Connect();
var assetId = Guid.NewGuid().ToString();
var assetData = new MemoryStream(new byte[] { 0x1, 0x2, 0x3, 0x4 });
Sut.Connect();
await sut.UploadAsync(assetId, 1, "suffix", assetData);
var readData = new MemoryStream();
await sut.DownloadAsync(assetId, 1, "suffix", readData);
Assert.Equal(assetData.ToArray(), readData.ToArray());
Assert.True(Directory.Exists(testFolder));
}
private static string CreateInvalidPath()

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

@ -0,0 +1,22 @@
// ==========================================================================
// GoogleCloudAssetStoreTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Infrastructure.Assets
{
internal class GoogleCloudAssetStoreTests : AssetStoreTests<GoogleCloudAssetStore>
{
public override GoogleCloudAssetStore CreateStore()
{
return new GoogleCloudAssetStore("squidex-test");
}
public override void Dispose()
{
}
}
}

2
tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj

@ -5,10 +5,12 @@
<RootNamespace>Squidex.Infrastructure</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Squidex.Infrastructure.GoogleCloud\Squidex.Infrastructure.GoogleCloud.csproj" />
<ProjectReference Include="..\..\src\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="4.19.3" />
<PackageReference Include="Google.Cloud.Storage.V1" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />

Loading…
Cancel
Save