Adds more assertions and tests and validates variations

This commit is contained in:
Shannon
2019-08-01 18:49:05 +10:00
parent 1095815878
commit 9538981730
4 changed files with 133 additions and 237 deletions

View File

@@ -101,7 +101,7 @@ namespace Umbraco.Core.Models
PropertyGroupCollection PropertyGroups { get; set; }
/// <summary>
/// Gets all local property types belonging to a group, across all local property groups.
/// Gets all local property types all local property groups or ungrouped.
/// </summary>
IEnumerable<PropertyType> PropertyTypes { get; }

View File

@@ -100,6 +100,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected void PersistNewBaseContentType(IContentTypeComposition entity)
{
ValidateVariations(entity);
var dto = ContentTypeFactory.BuildContentTypeDto(entity);
//Cannot add a duplicate content type
@@ -165,11 +167,11 @@ AND umbracoNode.nodeObjectType = @objectType",
foreach (var allowedContentType in entity.AllowedContentTypes)
{
Database.Insert(new ContentTypeAllowedContentTypeDto
{
Id = entity.Id,
AllowedId = allowedContentType.Id.Value,
SortOrder = allowedContentType.SortOrder
});
{
Id = entity.Id,
AllowedId = allowedContentType.Id.Value,
SortOrder = allowedContentType.SortOrder
});
}
@@ -216,6 +218,8 @@ AND umbracoNode.nodeObjectType = @objectType",
protected void PersistUpdatedBaseContentType(IContentTypeComposition entity)
{
ValidateVariations(entity);
var dto = ContentTypeFactory.BuildContentTypeDto(entity);
// ensure the alias is not used already
@@ -372,7 +376,7 @@ AND umbracoNode.id <> @id",
foreach (var propertyGroup in entity.PropertyGroups)
{
// insert or update group
var groupDto = PropertyGroupFactory.BuildGroupDto(propertyGroup,entity.Id);
var groupDto = PropertyGroupFactory.BuildGroupDto(propertyGroup, entity.Id);
var groupId = propertyGroup.HasIdentity
? Database.Update(groupDto)
: Convert.ToInt32(Database.Insert(groupDto));
@@ -390,7 +394,7 @@ AND umbracoNode.id <> @id",
//check if the content type variation has been changed
var contentTypeVariationDirty = entity.IsPropertyDirty("Variations");
var oldContentTypeVariation = (ContentVariation) dtoPk.Variations;
var oldContentTypeVariation = (ContentVariation)dtoPk.Variations;
var newContentTypeVariation = entity.Variations;
var contentTypeVariationChanging = contentTypeVariationDirty && oldContentTypeVariation != newContentTypeVariation;
if (contentTypeVariationChanging)
@@ -451,7 +455,7 @@ AND umbracoNode.id <> @id",
// via composition, with their original variations (ie not filtered by this
// content type variations - we need this true value to make decisions.
foreach (var propertyType in ((ContentTypeCompositionBase) entity).RawComposedPropertyTypes)
foreach (var propertyType in ((ContentTypeCompositionBase)entity).RawComposedPropertyTypes)
{
if (propertyType.VariesBySegment() || newContentTypeVariation.VariesBySegment())
throw new NotSupportedException(); // TODO: support this
@@ -520,6 +524,19 @@ AND umbracoNode.id <> @id",
CommonRepository.ClearCache(); // always
}
/// <summary>
/// Ensures that no property types are flagged for a variance that is not supported by the content type itself
/// </summary>
/// <param name="entity"></param>
private void ValidateVariations(IContentTypeComposition entity)
{
//if the entity does not vary at all, then the property cannot have a variance value greater than it
if (entity.Variations == ContentVariation.Nothing)
foreach (var prop in entity.PropertyTypes)
if (prop.Variations > entity.Variations)
throw new InvalidOperationException($"The property {prop.Alias} cannot have variations of {prop.Variations} with the content type variations of {entity.Variations}");
}
private IEnumerable<IContentTypeComposition> GetImpactedContentTypes(IContentTypeComposition contentType, IEnumerable<IContentTypeComposition> all)
{
var impact = new List<IContentTypeComposition>();
@@ -527,12 +544,12 @@ AND umbracoNode.id <> @id",
var tree = new Dictionary<int, List<IContentTypeComposition>>();
foreach (var x in all)
foreach (var y in x.ContentTypeComposition)
{
if (!tree.TryGetValue(y.Id, out var list))
list = tree[y.Id] = new List<IContentTypeComposition>();
list.Add(x);
}
foreach (var y in x.ContentTypeComposition)
{
if (!tree.TryGetValue(y.Id, out var list))
list = tree[y.Id] = new List<IContentTypeComposition>();
list.Add(x);
}
var nset = new List<IContentTypeComposition>();
do
@@ -574,7 +591,7 @@ AND umbracoNode.id <> @id",
// new property type, ignore
if (!oldVariations.TryGetValue(propertyType.Id, out var oldVariationB))
continue;
var oldVariation = (ContentVariation) oldVariationB; // NPoco cannot fetch directly
var oldVariation = (ContentVariation)oldVariationB; // NPoco cannot fetch directly
// only those property types that *actually* changed
var newVariation = propertyType.Variations;
@@ -638,7 +655,7 @@ AND umbracoNode.id <> @id",
var impactedL = impacted.Select(x => x.Id).ToList();
//Group by the "To" variation so we can bulk update in the correct batches
foreach(var grouping in propertyTypeChanges.GroupBy(x => x.Value.ToVariation))
foreach (var grouping in propertyTypeChanges.GroupBy(x => x.Value.ToVariation))
{
var propertyTypeIds = grouping.Select(x => x.Key).ToList();
var toVariation = grouping.Key;
@@ -1037,8 +1054,8 @@ AND umbracoNode.id <> @id",
{
var defaultLang = LanguageRepository.GetDefaultId();
//This will build up a query to get the property values of both the current and the published version so that we can check
//based on the current variance of each item to see if it's 'edited' value should be true/false.
//This will build up a query to get the property values of both the current and the published version so that we can check
//based on the current variance of each item to see if it's 'edited' value should be true/false.
var whereInArgsCount = propertyTypeIds.Count + (contentTypeIds?.Count ?? 0);
if (whereInArgsCount > 2000)
@@ -1077,7 +1094,7 @@ AND umbracoNode.id <> @id",
var editedDocument = new Dictionary<int, bool>();
var nodeId = -1;
var propertyTypeId = -1;
PropertyValueVersionDto pubRow = null;
//This is a reader (Query), we are not fetching this all into memory so we cannot make any changes during this iteration, we are just collecting data.
@@ -1087,7 +1104,7 @@ AND umbracoNode.id <> @id",
{
//make sure to reset on each node/property change
if (nodeId != row.NodeId || propertyTypeId != row.PropertyTypeId)
{
{
nodeId = row.NodeId;
propertyTypeId = row.PropertyTypeId;
pubRow = null;
@@ -1114,7 +1131,7 @@ AND umbracoNode.id <> @id",
else if (pubRow == null)
{
//this would mean that that this property is 'edited' since there is no published version
editedLanguageVersions.Add((row.NodeId, row.LanguageId), true);
editedLanguageVersions[(row.NodeId, row.LanguageId)] = true;
editedDocument[row.NodeId] = true;
}
//compare the property values, if they differ from versions then flag the current version as edited
@@ -1181,10 +1198,9 @@ AND umbracoNode.id <> @id",
toUpdate.Add(docVariations);
}
}
else
else if (ev.Key.langId.HasValue)
{
//the row doesn't exist but needs creating
//TODO: Does this ever happen?? Need to see if we can test this
//This should never happen! If a property culture is flagged as edited then the culture must exist at the document level
throw new PanicException($"The existing DocumentCultureVariationDto was not found for node {ev.Key.nodeId} and language {ev.Key.langId}");
}
}
@@ -1197,7 +1213,7 @@ AND umbracoNode.id <> @id",
}
//Now bulk update the umbracoDocument table
foreach(var editValue in editedDocument.GroupBy(x => x.Value))
foreach (var editValue in editedDocument.GroupBy(x => x.Value))
{
Database.Execute(Sql().Update<DocumentDto>(u => u.Set(x => x.Edited, editValue.Key))
.WhereIn<DocumentDto>(x => x.NodeId, editValue.Select(x => x.Key)));
@@ -1226,7 +1242,7 @@ AND umbracoNode.id <> @id",
}
private class PropertyValueVersionDto
{
{
public int VersionId { get; set; }
public int PropertyTypeId { get; set; }
public int? LanguageId { get; set; }

View File

@@ -1310,14 +1310,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
Name = content.GetCultureName(culture) ?? content.GetPublishName(culture),
Available = content.IsCultureAvailable(culture),
Published = content.IsCulturePublished(culture)
Published = content.IsCulturePublished(culture),
// note: can't use IsCultureEdited at that point - hasn't been updated yet - see PersistUpdatedItem
Edited = content.IsCultureAvailable(culture) &&
(!content.IsCulturePublished(culture) || (editedCultures != null && editedCultures.Contains(culture)))
};
// note: can't use IsCultureEdited at that point - hasn't been updated yet - see PersistUpdatedItem
dto.Edited = content.IsCultureAvailable(culture) &&
(!content.IsCulturePublished(culture) || (editedCultures != null && editedCultures.Contains(culture)));
yield return dto;
}

View File

@@ -110,7 +110,6 @@ namespace Umbraco.Tests.Services
[Test]
public void Change_Content_Type_Variation_Clears_Redirects()
{
//create content type with a property type that varies by culture
var contentType = MockedContentTypes.CreateBasicContentType();
contentType.Variations = ContentVariation.Nothing;
var contentCollection = new PropertyTypeCollection(true);
@@ -154,8 +153,7 @@ namespace Umbraco.Tests.Services
[Test]
public void Change_Content_Type_From_Invariant_Variant()
{
//create content type with a property type that varies by culture
{
var contentType = MockedContentTypes.CreateBasicContentType();
contentType.Variations = ContentVariation.Nothing;
var contentCollection = new PropertyTypeCollection(true);
@@ -205,7 +203,6 @@ namespace Umbraco.Tests.Services
[Test]
public void Change_Content_Type_From_Variant_Invariant()
{
//create content type with a property type that varies by culture
var contentType = MockedContentTypes.CreateBasicContentType();
contentType.Variations = ContentVariation.Culture;
var contentCollection = new PropertyTypeCollection(true);
@@ -264,39 +261,49 @@ namespace Umbraco.Tests.Services
}
[Test]
public void Change_Property_Type_From_Invariant_Variant()
public void Change_Property_Type_From_To_Variant_On_Invariant_Content_Type()
{
//create content type with a property type that varies by culture
var contentType = MockedContentTypes.CreateBasicContentType();
contentType.Variations = ContentVariation.Nothing;
var contentCollection = new PropertyTypeCollection(true);
contentCollection.Add(new PropertyType("test", ValueStorageType.Ntext)
{
Alias = "title",
Name = "Title",
Description = "",
Mandatory = false,
SortOrder = 1,
DataTypeId = -88,
Variations = ContentVariation.Nothing
});
contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 });
var properties = CreatePropertyCollection(("title", ContentVariation.Nothing));
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
ServiceContext.ContentTypeService.Save(contentType);
//change the property type to be variant
contentType.PropertyTypes.First().Variations = ContentVariation.Culture;
//Cannot change a property type to be variant if the content type itself is not variant
Assert.Throws<InvalidOperationException>(() => ServiceContext.ContentTypeService.Save(contentType));
}
[Test]
public void Change_Property_Type_From_Invariant_Variant()
{
var contentType = MockedContentTypes.CreateBasicContentType();
contentType.Variations = ContentVariation.Culture;
var properties = CreatePropertyCollection(("title", ContentVariation.Nothing));
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
ServiceContext.ContentTypeService.Save(contentType);
//create some content of this content type
IContent doc = MockedContent.CreateBasicContent(contentType);
doc.Name = "Home";
doc.SetCultureName("Home", "en-US");
doc.SetValue("title", "hello world");
ServiceContext.ContentService.Save(doc);
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
Assert.AreEqual("hello world", doc.GetValue("title"));
Assert.IsTrue(doc.IsCultureEdited("en-US")); //invariant prop changes show up on default lang
Assert.IsTrue(doc.Edited);
//change the property type to be variant
contentType.PropertyTypes.First().Variations = ContentVariation.Culture;
ServiceContext.ContentTypeService.Save(contentType);
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
Assert.AreEqual("hello world", doc.GetValue("title", "en-US"));
Assert.IsTrue(doc.IsCultureEdited("en-US"));
Assert.IsTrue(doc.Edited);
//change back property type to be invariant
contentType.PropertyTypes.First().Variations = ContentVariation.Nothing;
@@ -304,6 +311,8 @@ namespace Umbraco.Tests.Services
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
Assert.AreEqual("hello world", doc.GetValue("title"));
Assert.IsTrue(doc.IsCultureEdited("en-US")); //invariant prop changes show up on default lang
Assert.IsTrue(doc.Edited);
}
[Test]
@@ -470,28 +479,11 @@ namespace Umbraco.Tests.Services
CreateFrenchAndEnglishLangs();
var contentType = new ContentType(-1)
{
Alias = "contentType",
Name = "contentType",
Variations = ContentVariation.Culture
};
var contentType = CreateContentType(ContentVariation.Culture);
var properties = new PropertyTypeCollection(true)
{
new PropertyType("value1", ValueStorageType.Ntext)
{
Alias = "value1",
DataTypeId = -88,
Variations = ContentVariation.Culture
},
new PropertyType("value2", ValueStorageType.Ntext)
{
Alias = "value2",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
var properties = CreatePropertyCollection(
("value1", ContentVariation.Culture),
("value2", ContentVariation.Nothing));
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
ServiceContext.ContentTypeService.Save(contentType);
@@ -579,28 +571,11 @@ namespace Umbraco.Tests.Services
var languageFr = new Language("fr");
ServiceContext.LocalizationService.Save(languageFr);
var contentType = new ContentType(-1)
{
Alias = "contentType",
Name = "contentType",
Variations = ContentVariation.Nothing
};
var contentType = CreateContentType(ContentVariation.Nothing);
var properties = new PropertyTypeCollection(true)
{
new PropertyType("value1", ValueStorageType.Ntext)
{
Alias = "value1",
DataTypeId = -88,
Variations = ContentVariation.Nothing
},
new PropertyType("value2", ValueStorageType.Ntext)
{
Alias = "value2",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
var properties = CreatePropertyCollection(
("value1", ContentVariation.Nothing),
("value2", ContentVariation.Nothing));
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
ServiceContext.ContentTypeService.Save(contentType);
@@ -681,28 +656,11 @@ namespace Umbraco.Tests.Services
CreateFrenchAndEnglishLangs();
var contentType = new ContentType(-1)
{
Alias = "contentType",
Name = "contentType",
Variations = ContentVariation.Culture
};
var contentType = CreateContentType(ContentVariation.Culture);
var properties = new PropertyTypeCollection(true)
{
new PropertyType("value1", ValueStorageType.Ntext)
{
Alias = "value1",
DataTypeId = -88,
Variations = ContentVariation.Culture
},
new PropertyType("value2", ValueStorageType.Ntext)
{
Alias = "value2",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
var properties = CreatePropertyCollection(
("value1", ContentVariation.Culture),
("value2", ContentVariation.Nothing));
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
ServiceContext.ContentTypeService.Save(contentType);
@@ -780,29 +738,16 @@ namespace Umbraco.Tests.Services
}
[Test]
public void Change_Variations_SimpleContentType_VariantPropertyToInvariantAndBack_While_Publishing()
public void Change_Property_Variations_From_Variant_To_Invariant_And_Ensure_Edited_Values_Are_Renormalized()
{
// one simple content type, variant, with both variant and invariant properties
// can change an invariant property to variant and back
CreateFrenchAndEnglishLangs();
var contentType = new ContentType(-1)
{
Alias = "contentType",
Name = "contentType",
Variations = ContentVariation.Culture
};
var contentType = CreateContentType(ContentVariation.Culture);
var properties = new PropertyTypeCollection(true)
{
new PropertyType("value1", ValueStorageType.Ntext)
{
Alias = "value1",
DataTypeId = -88,
Variations = ContentVariation.Culture
}
};
var properties = CreatePropertyCollection(("value1", ContentVariation.Culture));
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
ServiceContext.ContentTypeService.Save(contentType);
@@ -902,54 +847,20 @@ namespace Umbraco.Tests.Services
CreateFrenchAndEnglishLangs();
var composing = new ContentType(-1)
{
Alias = "composing",
Name = "composing",
Variations = ContentVariation.Culture
};
var composing = CreateContentType(ContentVariation.Culture, "composing");
var properties1 = new PropertyTypeCollection(true)
{
new PropertyType("value11", ValueStorageType.Ntext)
{
Alias = "value11",
DataTypeId = -88,
Variations = ContentVariation.Culture
},
new PropertyType("value12", ValueStorageType.Ntext)
{
Alias = "value12",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
var properties1 = CreatePropertyCollection(
("value11", ContentVariation.Culture),
("value12", ContentVariation.Nothing));
composing.PropertyGroups.Add(new PropertyGroup(properties1) { Name = "Content" });
ServiceContext.ContentTypeService.Save(composing);
var composed = new ContentType(-1)
{
Alias = "composed",
Name = "composed",
Variations = ContentVariation.Culture
};
var composed = CreateContentType(ContentVariation.Culture, "composed");
var properties2 = new PropertyTypeCollection(true)
{
new PropertyType("value21", ValueStorageType.Ntext)
{
Alias = "value21",
DataTypeId = -88,
Variations = ContentVariation.Culture
},
new PropertyType("value22", ValueStorageType.Ntext)
{
Alias = "value22",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
var properties2 = CreatePropertyCollection(
("value21", ContentVariation.Culture),
("value22", ContentVariation.Nothing));
composed.PropertyGroups.Add(new PropertyGroup(properties2) { Name = "Content" });
composed.AddContentType(composing);
@@ -1031,81 +942,30 @@ namespace Umbraco.Tests.Services
CreateFrenchAndEnglishLangs();
var composing = new ContentType(-1)
{
Alias = "composing",
Name = "composing",
Variations = ContentVariation.Culture
};
var composing = CreateContentType(ContentVariation.Culture, "composing");
var properties1 = new PropertyTypeCollection(true)
{
new PropertyType("value11", ValueStorageType.Ntext)
{
Alias = "value11",
DataTypeId = -88,
Variations = ContentVariation.Culture
},
new PropertyType("value12", ValueStorageType.Ntext)
{
Alias = "value12",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
var properties1 = CreatePropertyCollection(
("value11", ContentVariation.Culture),
("value12", ContentVariation.Nothing));
composing.PropertyGroups.Add(new PropertyGroup(properties1) { Name = "Content" });
ServiceContext.ContentTypeService.Save(composing);
var composed1 = new ContentType(-1)
{
Alias = "composed1",
Name = "composed1",
Variations = ContentVariation.Culture
};
var composed1 = CreateContentType(ContentVariation.Culture, "composed1");
var properties2 = new PropertyTypeCollection(true)
{
new PropertyType("value21", ValueStorageType.Ntext)
{
Alias = "value21",
DataTypeId = -88,
Variations = ContentVariation.Culture
},
new PropertyType("value22", ValueStorageType.Ntext)
{
Alias = "value22",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
var properties2 = CreatePropertyCollection(
("value21", ContentVariation.Culture),
("value22", ContentVariation.Nothing));
composed1.PropertyGroups.Add(new PropertyGroup(properties2) { Name = "Content" });
composed1.AddContentType(composing);
ServiceContext.ContentTypeService.Save(composed1);
var composed2 = new ContentType(-1)
{
Alias = "composed2",
Name = "composed2",
Variations = ContentVariation.Nothing
};
var composed2 = CreateContentType(ContentVariation.Nothing, "composed2");
var properties3 = new PropertyTypeCollection(true)
{
new PropertyType("value31", ValueStorageType.Ntext)
{
Alias = "value31",
DataTypeId = -88,
Variations = ContentVariation.Nothing
},
new PropertyType("value32", ValueStorageType.Ntext)
{
Alias = "value32",
DataTypeId = -88,
Variations = ContentVariation.Nothing
}
};
var properties3 = CreatePropertyCollection(
("value31", ContentVariation.Nothing),
("value32", ContentVariation.Nothing));
composed2.PropertyGroups.Add(new PropertyGroup(properties3) { Name = "Content" });
composed2.AddContentType(composing);
@@ -1219,5 +1079,27 @@ namespace Umbraco.Tests.Services
var languageFr = new Language("fr");
ServiceContext.LocalizationService.Save(languageFr);
}
private IContentType CreateContentType(ContentVariation variance, string alias = "contentType") => new ContentType(-1)
{
Alias = alias,
Name = alias,
Variations = variance
};
private PropertyTypeCollection CreatePropertyCollection(params (string alias, ContentVariation variance)[] props)
{
var propertyCollection = new PropertyTypeCollection(true);
foreach (var (alias, variance) in props)
propertyCollection.Add(new PropertyType(alias, ValueStorageType.Ntext)
{
Alias = alias,
DataTypeId = -88,
Variations = variance
});
return propertyCollection;
}
}
}