Browse Source

Another migration fix.

pull/596/head
Sebastian 5 years ago
parent
commit
83d823a29a
  1. 9
      backend/src/Migrations/Migrations/MongoDb/AddAppIdToEventStream.cs
  2. 24
      backend/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs
  3. 2
      backend/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs
  4. 71
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/BackupAssetsTests.cs
  5. 24
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs

9
backend/src/Migrations/Migrations/MongoDb/AddAppIdToEventStream.cs

@ -67,6 +67,8 @@ namespace Migrations.Migrations.MongoDb
var domainType = eventStream.Substring(0, indexOfType); var domainType = eventStream.Substring(0, indexOfType);
var domainId = eventStream.Substring(indexOfId); var domainId = eventStream.Substring(indexOfId);
if (!eventStream.StartsWith("app-", StringComparison.OrdinalIgnoreCase))
{
var newDomainId = DomainId.Combine(DomainId.Create(appId), DomainId.Create(domainId)).ToString(); var newDomainId = DomainId.Combine(DomainId.Create(appId), DomainId.Create(domainId)).ToString();
var newStreamName = $"{domainType}-{newDomainId}"; var newStreamName = $"{domainType}-{newDomainId}";
@ -77,6 +79,13 @@ namespace Migrations.Migrations.MongoDb
var metadata = @event["Metadata"].AsBsonDocument; var metadata = @event["Metadata"].AsBsonDocument;
metadata["AggregateId"] = newDomainId; metadata["AggregateId"] = newDomainId;
}
}
foreach (var @event in document["Events"].AsBsonArray)
{
var metadata = @event["Metadata"].AsBsonDocument;
metadata.Remove("AppId"); metadata.Remove("AppId");
} }
} }

24
backend/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs

@ -6,12 +6,14 @@
// ========================================================================== // ==========================================================================
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Tags; using Squidex.Domain.Apps.Core.Tags;
using Squidex.Domain.Apps.Entities.Assets.State; using Squidex.Domain.Apps.Entities.Assets.State;
using Squidex.Domain.Apps.Entities.Backup; using Squidex.Domain.Apps.Entities.Backup;
using Squidex.Domain.Apps.Events.Assets; using Squidex.Domain.Apps.Events.Assets;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Assets;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
@ -122,21 +124,35 @@ namespace Squidex.Domain.Apps.Entities.Assets
await context.Writer.WriteJsonAsync(TagsFile, tags); await context.Writer.WriteJsonAsync(TagsFile, tags);
} }
private Task WriteAssetAsync(DomainId appId, DomainId assetId, long fileVersion, IBackupWriter writer) private async Task WriteAssetAsync(DomainId appId, DomainId assetId, long fileVersion, IBackupWriter writer)
{ {
return writer.WriteBlobAsync(GetName(assetId, fileVersion), stream => try
{
await writer.WriteBlobAsync(GetName(assetId, fileVersion), stream =>
{ {
return assetFileStore.DownloadAsync(appId, assetId, fileVersion, stream); return assetFileStore.DownloadAsync(appId, assetId, fileVersion, stream);
}); });
} }
catch (AssetNotFoundException)
{
return;
}
}
private Task ReadAssetAsync(DomainId appId, DomainId assetId, long fileVersion, IBackupReader reader) private async Task ReadAssetAsync(DomainId appId, DomainId assetId, long fileVersion, IBackupReader reader)
{ {
return reader.ReadBlobAsync(GetName(assetId, fileVersion), stream => try
{
await reader.ReadBlobAsync(GetName(assetId, fileVersion), stream =>
{ {
return assetFileStore.UploadAsync(appId, assetId, fileVersion, stream); return assetFileStore.UploadAsync(appId, assetId, fileVersion, stream);
}); });
} }
catch (FileNotFoundException)
{
return;
}
}
private static string GetName(DomainId assetId, long fileVersion) private static string GetName(DomainId assetId, long fileVersion)
{ {

2
backend/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs

@ -83,7 +83,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
{ {
var attachmentEntry = archive.GetEntry(ArchiveHelper.GetAttachmentPath(name)); var attachmentEntry = archive.GetEntry(ArchiveHelper.GetAttachmentPath(name));
if (attachmentEntry == null) if (attachmentEntry == null || attachmentEntry.Length == 0)
{ {
throw new FileNotFoundException("Cannot find attachment.", name); throw new FileNotFoundException("Cannot find attachment.", name);
} }

71
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/BackupAssetsTests.cs

@ -16,6 +16,7 @@ using Squidex.Domain.Apps.Entities.Assets.State;
using Squidex.Domain.Apps.Entities.Backup; using Squidex.Domain.Apps.Entities.Backup;
using Squidex.Domain.Apps.Events.Assets; using Squidex.Domain.Apps.Events.Assets;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Assets;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
using Xunit; using Xunit;
@ -81,7 +82,15 @@ namespace Squidex.Domain.Apps.Entities.Assets
{ {
var @event = new AssetCreated { AssetId = DomainId.NewGuid() }; var @event = new AssetCreated { AssetId = DomainId.NewGuid() };
await TestBackupEventAsync(@event, 0); await TestBackupAsync(@event, 0);
}
[Fact]
public async Task Should_backup_created_asset_with_missing_file()
{
var @event = new AssetCreated { AssetId = DomainId.NewGuid() };
await TestBackupFailedAsync(@event, 0);
} }
[Fact] [Fact]
@ -89,10 +98,18 @@ namespace Squidex.Domain.Apps.Entities.Assets
{ {
var @event = new AssetUpdated { AssetId = DomainId.NewGuid(), FileVersion = 3 }; var @event = new AssetUpdated { AssetId = DomainId.NewGuid(), FileVersion = 3 };
await TestBackupEventAsync(@event, @event.FileVersion); await TestBackupAsync(@event, @event.FileVersion);
} }
private async Task TestBackupEventAsync(AssetEvent @event, long version) [Fact]
public async Task Should_backup_updated_asset_with_missing_file()
{
var @event = new AssetUpdated { AssetId = DomainId.NewGuid(), FileVersion = 3 };
await TestBackupFailedAsync(@event, @event.FileVersion);
}
private async Task TestBackupAsync(AssetEvent @event, long version)
{ {
var assetStream = new MemoryStream(); var assetStream = new MemoryStream();
var assetId = @event.AssetId; var assetId = @event.AssetId;
@ -108,6 +125,22 @@ namespace Squidex.Domain.Apps.Entities.Assets
.MustHaveHappened(); .MustHaveHappened();
} }
private async Task TestBackupFailedAsync(AssetEvent @event, long version)
{
var assetStream = new MemoryStream();
var assetId = @event.AssetId;
var context = CreateBackupContext();
A.CallTo(() => context.Writer.WriteBlobAsync($"{assetId}_{version}.asset", A<Func<Stream, Task>>._))
.Invokes((string _, Func<Stream, Task> handler) => handler(assetStream));
A.CallTo(() => assetFileStore.DownloadAsync(appId.Id, assetId, version, assetStream, default, default))
.Throws(new AssetNotFoundException(assetId.ToString()));
await sut.BackupEventAsync(AppEvent(@event), context);
}
[Fact] [Fact]
public async Task Should_restore_created_asset() public async Task Should_restore_created_asset()
{ {
@ -116,6 +149,14 @@ namespace Squidex.Domain.Apps.Entities.Assets
await TestRestoreAsync(@event, 0); await TestRestoreAsync(@event, 0);
} }
[Fact]
public async Task Should_restore_created_asset_with_missing_file()
{
var @event = new AssetCreated { AssetId = DomainId.NewGuid() };
await TestRestoreFailedAsync(@event, 0);
}
[Fact] [Fact]
public async Task Should_restore_updated_asset() public async Task Should_restore_updated_asset()
{ {
@ -124,6 +165,14 @@ namespace Squidex.Domain.Apps.Entities.Assets
await TestRestoreAsync(@event, @event.FileVersion); await TestRestoreAsync(@event, @event.FileVersion);
} }
[Fact]
public async Task Should_restore_updated_asset_with_missing_file()
{
var @event = new AssetUpdated { AppId = appId, AssetId = DomainId.NewGuid(), FileVersion = 3 };
await TestRestoreFailedAsync(@event, @event.FileVersion);
}
private async Task TestRestoreAsync(AssetEvent @event, long version) private async Task TestRestoreAsync(AssetEvent @event, long version)
{ {
var assetStream = new MemoryStream(); var assetStream = new MemoryStream();
@ -140,6 +189,22 @@ namespace Squidex.Domain.Apps.Entities.Assets
.MustHaveHappened(); .MustHaveHappened();
} }
private async Task TestRestoreFailedAsync(AssetEvent @event, long version)
{
var assetStream = new MemoryStream();
var assetId = @event.AssetId;
var context = CreateRestoreContext();
A.CallTo(() => context.Reader.ReadBlobAsync($"{assetId}_{version}.asset", A<Func<Stream, Task>>._))
.Throws(new FileNotFoundException());
await sut.RestoreEventAsync(AppEvent(@event), context);
A.CallTo(() => assetFileStore.UploadAsync(appId.Id, assetId, version, assetStream, default))
.MustNotHaveHappened();
}
[Fact] [Fact]
public async Task Should_restore_states_for_all_assets() public async Task Should_restore_states_for_all_assets()
{ {

24
backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs

@ -40,6 +40,30 @@ namespace Squidex.Domain.Apps.Entities.Backup
formatter = new DefaultEventDataFormatter(typeNameRegistry, serializer); formatter = new DefaultEventDataFormatter(typeNameRegistry, serializer);
} }
[Fact]
public async Task Should_not_write_blob_if_handler_failed()
{
var file = "File.json";
await TestReaderWriterAsync(BackupVersion.V1, async writer =>
{
try
{
await writer.WriteBlobAsync(file, _ =>
{
throw new InvalidOperationException();
});
}
catch
{
return;
}
}, async reader =>
{
await Assert.ThrowsAsync<FileNotFoundException>(() => ReadGuidAsync(reader, file));
});
}
[Fact] [Fact]
public async Task Should_read_and_write_json_async() public async Task Should_read_and_write_json_async()
{ {

Loading…
Cancel
Save