Browse Source

Delete assets permanently. (#674)

pull/677/head
Sebastian Stehle 5 years ago
committed by GitHub
parent
commit
ca82d281e1
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 64
      backend/src/Squidex.Domain.Apps.Entities/Assets/AssetPermanentDeleter.cs
  2. 5
      backend/src/Squidex.Domain.Apps.Entities/Assets/RecursiveDeleter.cs
  3. 2
      backend/src/Squidex/Areas/IdentityServer/Controllers/Setup/SetupController.cs
  4. 6
      backend/src/Squidex/Config/Domain/AssetServices.cs
  5. 35
      backend/src/Squidex/appsettings.json
  6. 99
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetPermanentDeleterTests.cs
  7. 3
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RecursiveDeleterTests.cs

64
backend/src/Squidex.Domain.Apps.Entities/Assets/AssetPermanentDeleter.cs

@ -0,0 +1,64 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Assets;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Entities.Assets
{
public sealed class AssetPermanentDeleter : IEventConsumer
{
private readonly IAssetFileStore assetFileStore;
private readonly string? deletedType;
public string Name
{
get => GetType().Name;
}
public string EventsFilter
{
get => "^asset-";
}
public AssetPermanentDeleter(IAssetFileStore assetFileStore, TypeNameRegistry typeNameRegistry)
{
Guard.NotNull(assetFileStore, nameof(assetFileStore));
this.assetFileStore = assetFileStore;
deletedType = typeNameRegistry?.GetName<AssetDeleted>();
}
public bool Handles(StoredEvent @event)
{
return @event.Data.Type == deletedType;
}
public async Task On(Envelope<IEvent> @event)
{
if (@event.Payload is AssetDeleted assetDeleted)
{
for (var version = 0; version < @event.Headers.EventStreamNumber(); version++)
{
try
{
await assetFileStore.DeleteAsync(assetDeleted.AppId.Id, assetDeleted.AssetId, version);
}
catch (AssetNotFoundException)
{
continue;
}
}
}
}
}
}

5
backend/src/Squidex.Domain.Apps.Entities/Assets/RecursiveDeleter.cs

@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
private readonly IAssetRepository assetRepository;
private readonly IAssetFolderRepository assetFolderRepository;
private readonly ISemanticLog log;
private readonly string folderDeletedType;
private readonly string? folderDeletedType;
public string Name
{
@ -46,7 +46,6 @@ namespace Squidex.Domain.Apps.Entities.Assets
Guard.NotNull(commandBus, nameof(commandBus));
Guard.NotNull(assetRepository, nameof(assetRepository));
Guard.NotNull(assetFolderRepository, nameof(assetFolderRepository));
Guard.NotNull(typeNameRegistry, nameof(typeNameRegistry));
Guard.NotNull(log, nameof(log));
this.commandBus = commandBus;
@ -54,7 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
this.assetFolderRepository = assetFolderRepository;
this.log = log;
folderDeletedType = typeNameRegistry.GetName<AssetFolderDeleted>();
folderDeletedType = typeNameRegistry?.GetName<AssetFolderDeleted>();
}
public bool Handles(StoredEvent @event)

2
backend/src/Squidex/Areas/IdentityServer/Controllers/Setup/SetupController.cs

@ -103,7 +103,7 @@ namespace Squidex.Areas.IdentityServer.Controllers.Setup
var result = new SetupVM
{
BaseUrlConfigured = urlGenerator.BuildUrl(),
BaseUrlConfigured = urlGenerator.BuildUrl(string.Empty, false),
BaseUrlCurrent = $"{request.Scheme}://{request.Host}",
ErrorMessage = errorMessage,
EverybodyCanCreateApps = !uiOptions.OnlyAdminsCanCreateApps,

6
backend/src/Squidex/Config/Domain/AssetServices.cs

@ -38,6 +38,12 @@ namespace Squidex.Config.Domain
.As<IEventConsumer>();
}
if (config.GetValue<bool>("assets:deletePermanent"))
{
services.AddTransientAs<AssetPermanentDeleter>()
.As<IEventConsumer>();
}
services.AddTransientAs<AssetDomainObject>()
.AsSelf();

35
backend/src/Squidex/appsettings.json

@ -302,7 +302,12 @@
/*
* True to delete assets recursively.
*/
"deleteRecursive": true
"deleteRecursive": true,
/*
* True to delete assets files permanently.
*/
"deletePermanent": false
},
"logging": {
@ -699,38 +704,38 @@
/*
* Set to true to rebuild apps.
*/
"apps": false,
"apps": false,
/*
/*
* Set to true to rebuild assets.
*/
"assets": false,
"assets": false,
/*
/*
* Set to true to create dummy asset files if they do not exist. Useful when a backup fail.
*/
"assetFiles": false,
"assetFiles": false,
/*
/*
* Set to true to rebuild contents.
*/
"contents": false,
"contents": false,
/*
/*
* Set to true to rebuild rules.
*/
"rules": false,
"rules": false,
/*
/*
* Set to true to rebuild schemas.
*/
"schemas": false,
"schemas": false,
/*
/*
* Set to true to rebuild indexes.
*/
"indexes": false
},
"indexes": false
},
/*"
* A list of configuration values that should be exposed from the info endpoint and in the UI.

99
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetPermanentDeleterTests.cs

@ -0,0 +1,99 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Assets;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Assets
{
public class AssetPermanentDeleterTests
{
private readonly IAssetFileStore assetFiletore = A.Fake<IAssetFileStore>();
private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app");
private readonly AssetPermanentDeleter sut;
public AssetPermanentDeleterTests()
{
var typeNameRegistry = new TypeNameRegistry().Map(typeof(AssetDeleted));
sut = new AssetPermanentDeleter(assetFiletore, typeNameRegistry);
}
[Fact]
public void Should_return_assets_filter_for_events_filter()
{
IEventConsumer consumer = sut;
Assert.Equal("^asset-", consumer.EventsFilter);
}
[Fact]
public async Task Should_do_nothing_on_clear()
{
IEventConsumer consumer = sut;
await consumer.ClearAsync();
}
[Fact]
public void Should_return_type_name_for_name()
{
IEventConsumer consumer = sut;
Assert.Equal(nameof(AssetPermanentDeleter), consumer.Name);
}
[Fact]
public async Task Should_delete_assets_for_all_versions()
{
var @event = new AssetDeleted { AppId = appId, AssetId = DomainId.NewGuid() };
await sut.On(Envelope.Create(@event).SetEventStreamNumber(2));
A.CallTo(() => assetFiletore.DeleteAsync(appId.Id, @event.AssetId, 0))
.MustHaveHappened();
A.CallTo(() => assetFiletore.DeleteAsync(appId.Id, @event.AssetId, 1))
.MustHaveHappened();
}
[Fact]
public async Task Should_ignore_not_found_assets()
{
var @event = new AssetDeleted { AppId = appId, AssetId = DomainId.NewGuid() };
A.CallTo(() => assetFiletore.DeleteAsync(appId.Id, @event.AssetId, 0))
.Throws(new AssetNotFoundException("fileName"));
await sut.On(Envelope.Create(@event).SetEventStreamNumber(2));;
A.CallTo(() => assetFiletore.DeleteAsync(appId.Id, @event.AssetId, 1))
.MustHaveHappened();
}
[Fact]
public async Task Should_not_ignore_exceptions()
{
var @event = new AssetDeleted { AppId = appId, AssetId = DomainId.NewGuid() };
A.CallTo(() => assetFiletore.DeleteAsync(appId.Id, @event.AssetId, 0))
.Throws(new InvalidOperationException());
await Assert.ThrowsAsync<InvalidOperationException>(() => sut.On(Envelope.Create(@event).SetEventStreamNumber(2)));
A.CallTo(() => assetFiletore.DeleteAsync(appId.Id, @event.AssetId, 1))
.MustNotHaveHappened();
}
}
}

3
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RecursiveDeleterTests.cs

@ -23,7 +23,6 @@ namespace Squidex.Domain.Apps.Entities.Assets
{
public class RecursiveDeleterTests
{
private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry();
private readonly ISemanticLog log = A.Fake<ISemanticLog>();
private readonly IAssetRepository assetRepository = A.Fake<IAssetRepository>();
private readonly IAssetFolderRepository assetFolderRepository = A.Fake<IAssetFolderRepository>();
@ -33,7 +32,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
public RecursiveDeleterTests()
{
typeNameRegistry.Map(typeof(AssetFolderDeleted));
var typeNameRegistry = new TypeNameRegistry().Map(typeof(AssetFolderDeleted));
sut = new RecursiveDeleter(commandBus, assetRepository, assetFolderRepository, typeNameRegistry, log);
}

Loading…
Cancel
Save