mirror of https://github.com/Squidex/squidex.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
8.0 KiB
218 lines
8.0 KiB
// ==========================================================================
|
|
// Squidex Headless CMS
|
|
// ==========================================================================
|
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
// All rights reserved. Licensed under the MIT license.
|
|
// ==========================================================================
|
|
|
|
using Squidex.Domain.Apps.Core.Schemas;
|
|
using Squidex.Domain.Apps.Events;
|
|
using Squidex.Domain.Apps.Events.Schemas;
|
|
using Squidex.Infrastructure;
|
|
|
|
namespace Squidex.Domain.Apps.Core.EventSynchronization
|
|
{
|
|
public static class SchemaSynchronizer
|
|
{
|
|
public static IEnumerable<SchemaEvent> Synchronize(this Schema source, Schema? target, Func<long> idGenerator,
|
|
SchemaSynchronizationOptions? options = null)
|
|
{
|
|
Guard.NotNull(source);
|
|
Guard.NotNull(idGenerator);
|
|
|
|
if (target == null)
|
|
{
|
|
yield return new SchemaDeleted();
|
|
}
|
|
else
|
|
{
|
|
options ??= new SchemaSynchronizationOptions();
|
|
|
|
if (!source.Properties.Equals(target.Properties))
|
|
{
|
|
yield return new SchemaUpdated { Properties = target.Properties };
|
|
}
|
|
|
|
if (!source.Category.StringEquals(target.Category))
|
|
{
|
|
yield return new SchemaCategoryChanged { Name = target.Category };
|
|
}
|
|
|
|
if (!source.Scripts.Equals(target.Scripts))
|
|
{
|
|
yield return new SchemaScriptsConfigured { Scripts = target.Scripts };
|
|
}
|
|
|
|
if (!source.PreviewUrls.EqualsDictionary(target.PreviewUrls))
|
|
{
|
|
yield return new SchemaPreviewUrlsConfigured { PreviewUrls = target.PreviewUrls };
|
|
}
|
|
|
|
if (source.IsPublished != target.IsPublished)
|
|
{
|
|
yield return target.IsPublished ?
|
|
new SchemaPublished() :
|
|
new SchemaUnpublished();
|
|
}
|
|
|
|
var events = SyncFields(source.FieldCollection, target.FieldCollection, idGenerator, CanUpdateRoot, options);
|
|
|
|
foreach (var @event in events)
|
|
{
|
|
yield return @event;
|
|
}
|
|
|
|
if (!source.FieldsInLists.Equals(target.FieldsInLists))
|
|
{
|
|
yield return new SchemaUIFieldsConfigured { FieldsInLists = target.FieldsInLists };
|
|
}
|
|
|
|
if (!source.FieldsInReferences.Equals(target.FieldsInReferences))
|
|
{
|
|
yield return new SchemaUIFieldsConfigured { FieldsInReferences = target.FieldsInReferences };
|
|
}
|
|
|
|
if (!source.FieldRules.Equals(target.FieldRules))
|
|
{
|
|
yield return new SchemaFieldRulesConfigured { FieldRules = target.FieldRules };
|
|
}
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<ParentFieldEvent> SyncFields<T>(
|
|
FieldCollection<T> source,
|
|
FieldCollection<T> target,
|
|
Func<long> idGenerator,
|
|
Func<T, T, bool> canUpdate,
|
|
SchemaSynchronizationOptions options) where T : class, IField
|
|
{
|
|
var sourceIds = source.Ordered.Select(x => x.NamedId()).ToList();
|
|
|
|
if (!options.NoFieldDeletion)
|
|
{
|
|
foreach (var sourceField in source.Ordered)
|
|
{
|
|
if (!target.ByName.TryGetValue(sourceField.Name, out _))
|
|
{
|
|
var id = sourceField.NamedId();
|
|
|
|
sourceIds.Remove(id);
|
|
|
|
yield return new FieldDeleted { FieldId = id };
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var targetField in target.Ordered)
|
|
{
|
|
NamedId<long>? id = null;
|
|
|
|
var canCreateField = true;
|
|
|
|
if (source.ByName.TryGetValue(targetField.Name, out var sourceField))
|
|
{
|
|
canCreateField = false;
|
|
|
|
id = sourceField.NamedId();
|
|
|
|
if (canUpdate(sourceField, targetField))
|
|
{
|
|
if (!sourceField.RawProperties.Equals(targetField.RawProperties as object))
|
|
{
|
|
yield return new FieldUpdated { FieldId = id, Properties = targetField.RawProperties };
|
|
}
|
|
}
|
|
else if (!sourceField.IsLocked && !options.NoFieldRecreation)
|
|
{
|
|
canCreateField = true;
|
|
|
|
sourceIds.Remove(id);
|
|
|
|
yield return new FieldDeleted { FieldId = id };
|
|
}
|
|
}
|
|
|
|
if (canCreateField)
|
|
{
|
|
var partitioning = (string?)null;
|
|
|
|
if (targetField is IRootField rootField)
|
|
{
|
|
partitioning = rootField.Partitioning.Key;
|
|
}
|
|
|
|
id = NamedId.Of(idGenerator(), targetField.Name);
|
|
|
|
yield return new FieldAdded
|
|
{
|
|
Name = targetField.Name,
|
|
Partitioning = partitioning,
|
|
Properties = targetField.RawProperties,
|
|
FieldId = id
|
|
};
|
|
|
|
sourceIds.Add(id);
|
|
}
|
|
|
|
if (id != null && (sourceField == null || CanUpdate(sourceField, targetField)))
|
|
{
|
|
if (!targetField.IsLocked.BoolEquals(sourceField?.IsLocked))
|
|
{
|
|
yield return new FieldLocked { FieldId = id };
|
|
}
|
|
|
|
if (!targetField.IsHidden.BoolEquals(sourceField?.IsHidden))
|
|
{
|
|
yield return targetField.IsHidden ?
|
|
new FieldHidden { FieldId = id } :
|
|
new FieldShown { FieldId = id };
|
|
}
|
|
|
|
if (!targetField.IsDisabled.BoolEquals(sourceField?.IsDisabled))
|
|
{
|
|
yield return targetField.IsDisabled ?
|
|
new FieldDisabled { FieldId = id } :
|
|
new FieldEnabled { FieldId = id };
|
|
}
|
|
|
|
if (sourceField is null or IArrayField && targetField is IArrayField targetArrayField)
|
|
{
|
|
var fields = (sourceField as IArrayField)?.FieldCollection ?? FieldCollection<NestedField>.Empty;
|
|
|
|
var events = SyncFields(fields, targetArrayField.FieldCollection, idGenerator, CanUpdate, options);
|
|
|
|
foreach (var @event in events)
|
|
{
|
|
@event.ParentFieldId = id;
|
|
|
|
yield return @event;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sourceIds.Count > 1)
|
|
{
|
|
var sourceNames = sourceIds.Select(x => x.Name).ToHashSet();
|
|
var targetNames = target.Ordered.Select(x => x.Name).ToHashSet();
|
|
|
|
if (sourceNames.SetEquals(targetNames) && !sourceNames.SequenceEqual(targetNames))
|
|
{
|
|
var fieldIds = targetNames.Select(x => sourceIds.Find(y => y.Name == x)!.Id).ToArray();
|
|
|
|
yield return new SchemaFieldsReordered { FieldIds = fieldIds };
|
|
}
|
|
}
|
|
}
|
|
|
|
private static bool CanUpdateRoot(IRootField source, IRootField target)
|
|
{
|
|
return CanUpdate(source, target) && source.Partitioning == target.Partitioning;
|
|
}
|
|
|
|
private static bool CanUpdate(IField source, IField target)
|
|
{
|
|
return !source.IsLocked && source.Name == target.Name && source.RawProperties.TypeEquals(target.RawProperties);
|
|
}
|
|
}
|
|
}
|
|
|