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