mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
16 changed files with 449 additions and 45 deletions
@ -0,0 +1,75 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Squidex.Domain.Apps.Entities.Backup; |
||||
|
using Squidex.Domain.Apps.Events.Rules; |
||||
|
using Squidex.Extensions.Actions.Script; |
||||
|
using Squidex.Flows; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
using Squidex.Infrastructure.Json; |
||||
|
using Squidex.Infrastructure.Json.Objects; |
||||
|
using Squidex.Infrastructure.Reflection; |
||||
|
|
||||
|
namespace Squidex.Extensions.Actions; |
||||
|
|
||||
|
public sealed class RuleEventMigrator(TypeRegistry typeRegistry, IJsonSerializer serializer) : IEventMigrator |
||||
|
{ |
||||
|
private readonly string[] migratedEvents = |
||||
|
[ |
||||
|
typeRegistry.GetName<IEvent>(typeof(RuleCreated)), |
||||
|
typeRegistry.GetName<IEvent>(typeof(RuleUpdated)), |
||||
|
]; |
||||
|
|
||||
|
public string? MigrateEvent(string type, string json) |
||||
|
{ |
||||
|
if (!migratedEvents.Contains(type)) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
var parsed = serializer.Deserialize<JsonValue>(json); |
||||
|
|
||||
|
var isChanged = false; |
||||
|
void Handle(JsonValue value) |
||||
|
{ |
||||
|
if (value.Type == JsonValueType.Object) |
||||
|
{ |
||||
|
var obj = value.AsObject; |
||||
|
foreach (var (key, nested) in obj) |
||||
|
{ |
||||
|
if (key == "$type" && nested.Value is string flowStepType) |
||||
|
{ |
||||
|
if (!typeRegistry.TryGetType<FlowStep>(flowStepType, out _)) |
||||
|
{ |
||||
|
isChanged = true; |
||||
|
obj["$type"] = typeRegistry.GetName<FlowStep, ScriptFlowStep>(); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Handle(nested); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else if (value.Type == JsonValueType.Array) |
||||
|
{ |
||||
|
foreach (var item in value.AsArray) |
||||
|
{ |
||||
|
Handle(item); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Handle(parsed); |
||||
|
if (!isChanged) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return serializer.Serialize(parsed); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using Microsoft.Extensions.Configuration; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Squidex.Domain.Apps.Entities.Backup; |
||||
|
using Squidex.Infrastructure.Plugins; |
||||
|
|
||||
|
namespace Squidex.Extensions.Actions; |
||||
|
|
||||
|
public sealed class RuleEventPlugin : IPlugin |
||||
|
{ |
||||
|
public void ConfigureServices(IServiceCollection services, IConfiguration config) |
||||
|
{ |
||||
|
services.AddTransientAs<RuleEventMigrator>() |
||||
|
.As<IEventMigrator>(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,78 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Migrations.OldEvents; |
||||
|
using Squidex.Domain.Apps.Core.Rules.Deprecated; |
||||
|
using Squidex.Domain.Apps.Entities.Backup; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
using Squidex.Infrastructure.Json; |
||||
|
using Squidex.Infrastructure.Json.Objects; |
||||
|
using Squidex.Infrastructure.Reflection; |
||||
|
|
||||
|
namespace Migrations.Migrations; |
||||
|
|
||||
|
public sealed class OldRuleEventMigrator(TypeRegistry typeRegistry, IJsonSerializer serializer) : IEventMigrator |
||||
|
{ |
||||
|
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
|
private readonly string[] migratedEvents = |
||||
|
[ |
||||
|
typeRegistry.GetName<IEvent>(typeof(RuleCreated)), |
||||
|
typeRegistry.GetName<IEvent>(typeof(RuleUpdated)), |
||||
|
]; |
||||
|
|
||||
|
public string? MigrateEvent(string type, string json) |
||||
|
{ |
||||
|
if (!migratedEvents.Contains(type)) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
var parsed = serializer.Deserialize<JsonValue>(json); |
||||
|
|
||||
|
var isChanged = false; |
||||
|
void Handle(JsonValue value) |
||||
|
{ |
||||
|
if (value.Type == JsonValueType.Object) |
||||
|
{ |
||||
|
var obj = value.AsObject; |
||||
|
foreach (var (key, nested) in obj) |
||||
|
{ |
||||
|
if (key == "actionType" && nested.Value is string flowStepType) |
||||
|
{ |
||||
|
if (!typeRegistry.TryGetType<RuleAction>(flowStepType, out _)) |
||||
|
{ |
||||
|
isChanged = true; |
||||
|
obj["actionType"] = "Webhook"; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Handle(nested); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else if (value.Type == JsonValueType.Array) |
||||
|
{ |
||||
|
foreach (var item in value.AsArray) |
||||
|
{ |
||||
|
Handle(item); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Handle(parsed); |
||||
|
if (!isChanged) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return serializer.Serialize(parsed); |
||||
|
} |
||||
|
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Backup; |
||||
|
|
||||
|
public interface IEventMigrator |
||||
|
{ |
||||
|
string? MigrateEvent(string type, string json); |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
namespace Squidex.Infrastructure.Reflection; |
||||
|
|
||||
|
public sealed class DelegateTypeProvider(Action<TypeRegistry> action) : ITypeProvider |
||||
|
{ |
||||
|
public void Map(TypeRegistry typeRegistry) |
||||
|
{ |
||||
|
action(typeRegistry); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,172 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Squidex.Domain.Apps.Core.TestHelpers; |
||||
|
using Squidex.Domain.Apps.Events.Rules; |
||||
|
using Squidex.Extensions.Actions; |
||||
|
using Squidex.Extensions.Actions.Script; |
||||
|
using Squidex.Extensions.Actions.Webhook; |
||||
|
using Squidex.Flows; |
||||
|
using Squidex.Flows.Internal; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
using Squidex.Infrastructure.Json; |
||||
|
using Squidex.Infrastructure.Reflection; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Rules; |
||||
|
|
||||
|
public class RuleEventMigratorTests |
||||
|
{ |
||||
|
private readonly TypeRegistry typeRegistrySerializer = new TypeRegistry(); |
||||
|
private readonly TypeRegistry typeRegistryMigrator = new TypeRegistry(); |
||||
|
private readonly IJsonSerializer serializer; |
||||
|
private readonly RuleEventMigrator sut; |
||||
|
|
||||
|
public RuleEventMigratorTests() |
||||
|
{ |
||||
|
typeRegistryMigrator.Add<IEvent, RuleCreated>("RuleCreated"); |
||||
|
typeRegistryMigrator.Add<IEvent, RuleUpdated>("RuleUpdated"); |
||||
|
typeRegistryMigrator.Add<FlowStep, ScriptFlowStep>("Script"); |
||||
|
|
||||
|
typeRegistrySerializer.Add<FlowStep, ScriptFlowStep>("Script"); |
||||
|
typeRegistrySerializer.Add<FlowStep, WebhookFlowStep>("Webhook"); |
||||
|
|
||||
|
// Create the serializer after the types have been registered.
|
||||
|
serializer = TestUtils.CreateSerializer(typeRegistrySerializer); |
||||
|
|
||||
|
sut = new RuleEventMigrator(typeRegistryMigrator, serializer); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_not_migrate_rule_created_event_if_flow_step_is_known() |
||||
|
{ |
||||
|
typeRegistryMigrator.Add<FlowStep, WebhookFlowStep>("Webhook"); |
||||
|
|
||||
|
var @event = new RuleCreated |
||||
|
{ |
||||
|
Flow = new FlowDefinition |
||||
|
{ |
||||
|
Steps = new Dictionary<Guid, FlowStepDefinition> |
||||
|
{ |
||||
|
[Guid.Empty] = new FlowStepDefinition |
||||
|
{ |
||||
|
Step = new WebhookFlowStep(), |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
var json = serializer.Serialize(@event, true); |
||||
|
|
||||
|
var result = sut.MigrateEvent(typeRegistryMigrator.GetName<IEvent>(@event.GetType()), json); |
||||
|
|
||||
|
Assert.Null(result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_migrate_rule_created_event() |
||||
|
{ |
||||
|
var @event = new RuleCreated |
||||
|
{ |
||||
|
Flow = new FlowDefinition |
||||
|
{ |
||||
|
Steps = new Dictionary<Guid, FlowStepDefinition> |
||||
|
{ |
||||
|
[Guid.Empty] = new FlowStepDefinition |
||||
|
{ |
||||
|
Step = new WebhookFlowStep(), |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
var json = serializer.Serialize(@event, true); |
||||
|
|
||||
|
var resultJson = sut.MigrateEvent(typeRegistryMigrator.GetName<IEvent>(@event.GetType()), json); |
||||
|
var resultEvent = serializer.Deserialize<RuleCreated>(resultJson!); |
||||
|
|
||||
|
resultEvent.Should().BeEquivalentTo( |
||||
|
new RuleCreated |
||||
|
{ |
||||
|
Flow = new FlowDefinition |
||||
|
{ |
||||
|
Steps = new Dictionary<Guid, FlowStepDefinition> |
||||
|
{ |
||||
|
[Guid.Empty] = new FlowStepDefinition |
||||
|
{ |
||||
|
Step = new WebhookFlowStep(), |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
options => options.RespectingDeclaredTypes()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_not_migrate_rule_updated_event_if_flow_step_is_known() |
||||
|
{ |
||||
|
typeRegistryMigrator.Add<FlowStep, WebhookFlowStep>("Webhook"); |
||||
|
|
||||
|
var @event = new RuleUpdated |
||||
|
{ |
||||
|
Flow = new FlowDefinition |
||||
|
{ |
||||
|
Steps = new Dictionary<Guid, FlowStepDefinition> |
||||
|
{ |
||||
|
[Guid.Empty] = new FlowStepDefinition |
||||
|
{ |
||||
|
Step = new WebhookFlowStep(), |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
var json = serializer.Serialize(@event, true); |
||||
|
|
||||
|
var result = sut.MigrateEvent(typeRegistryMigrator.GetName<IEvent>(@event.GetType()), json); |
||||
|
|
||||
|
Assert.Null(result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_migrate_rule_updated_event() |
||||
|
{ |
||||
|
var @event = new RuleUpdated |
||||
|
{ |
||||
|
Flow = new FlowDefinition |
||||
|
{ |
||||
|
Steps = new Dictionary<Guid, FlowStepDefinition> |
||||
|
{ |
||||
|
[Guid.Empty] = new FlowStepDefinition |
||||
|
{ |
||||
|
Step = new WebhookFlowStep(), |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
var json = serializer.Serialize(@event, true); |
||||
|
|
||||
|
var resultJson = sut.MigrateEvent(typeRegistryMigrator.GetName<IEvent>(@event.GetType()), json); |
||||
|
var resultEvent = serializer.Deserialize<RuleCreated>(resultJson!); |
||||
|
|
||||
|
resultEvent.Should().BeEquivalentTo( |
||||
|
new RuleUpdated |
||||
|
{ |
||||
|
Flow = new FlowDefinition |
||||
|
{ |
||||
|
Steps = new Dictionary<Guid, FlowStepDefinition> |
||||
|
{ |
||||
|
[Guid.Empty] = new FlowStepDefinition |
||||
|
{ |
||||
|
Step = new ScriptFlowStep(), |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
options => options.RespectingDeclaredTypes()); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue