Headless CMS and Content Managment Hub
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

// ==========================================================================
// 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);
}
}
}