Fixes mapping tests, simplifies property group mappings, adds another test to test composition mappings.

This commit is contained in:
Shannon
2015-07-13 13:42:40 +02:00
parent ae32f49243
commit 1e8bee9826
6 changed files with 332 additions and 194 deletions

View File

@@ -227,17 +227,7 @@ namespace Umbraco.Tests.Models.Mapping
.Returns(new[] { new TextboxPropertyEditor() });
var contentType = MockedContentTypes.CreateTextpageContentType();
//ensure everything has ids
contentType.Id = 1234;
var itemid = 8888;
foreach (var propertyGroup in contentType.CompositionPropertyGroups)
{
propertyGroup.Id = itemid++;
}
foreach (var propertyType in contentType.CompositionPropertyTypes)
{
propertyType.Id = itemid++;
}
MockedContentTypes.EnsureAllIds(contentType, 8888);
//Act
@@ -289,6 +279,94 @@ namespace Umbraco.Tests.Models.Mapping
}
[Test]
public void IContentTypeComposition_To_ContentTypeDisplay()
{
//Arrange
// setup the mocks to return the data we want to test against...
// for any call to GetPreValuesCollectionByDataTypeId just return an empty dictionary for now
// TODO: but we'll need to change this to return some pre-values to test the mappings
_dataTypeService.Setup(x => x.GetPreValuesCollectionByDataTypeId(It.IsAny<int>()))
.Returns(new PreValueCollection(new Dictionary<string, PreValue>()));
//return a textbox property editor for any requested editor by alias
_propertyEditorResolver.Setup(resolver => resolver.GetByAlias(It.IsAny<string>()))
.Returns(new TextboxPropertyEditor());
//for testing, just return a list of whatever property editors we want
_propertyEditorResolver.Setup(resolver => resolver.PropertyEditors)
.Returns(new[] { new TextboxPropertyEditor() });
var ctMain = MockedContentTypes.CreateSimpleContentType();
//not assigned to tab
ctMain.AddPropertyType(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
{
Alias = "umbracoUrlName", Name = "Slug", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
});
MockedContentTypes.EnsureAllIds(ctMain, 8888);
var ctChild1 = MockedContentTypes.CreateSimpleContentType("child1", "Child 1", ctMain, true);
MockedContentTypes.EnsureAllIds(ctChild1, 7777);
var contentType = MockedContentTypes.CreateSimpleContentType("child2", "Child 2", ctChild1, true, "CustomGroup");
//not assigned to tab
contentType.AddPropertyType(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext)
{
Alias = "umbracoUrlAlias", Name = "AltUrl", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88
});
MockedContentTypes.EnsureAllIds(contentType, 6666);
//Act
var result = Mapper.Map<ContentTypeDisplay>(contentType);
//Assert
Assert.AreEqual(contentType.Alias, result.Alias);
Assert.AreEqual(contentType.Description, result.Description);
Assert.AreEqual(contentType.Icon, result.Icon);
Assert.AreEqual(contentType.Id, result.Id);
Assert.AreEqual(contentType.Name, result.Name);
Assert.AreEqual(contentType.ParentId, result.ParentId);
Assert.AreEqual(contentType.Path, result.Path);
Assert.AreEqual(contentType.Thumbnail, result.Thumbnail);
Assert.AreEqual(contentType.IsContainer, result.IsContainer);
Assert.AreEqual(contentType.CreateDate, result.CreateDate);
Assert.AreEqual(contentType.UpdateDate, result.UpdateDate);
Assert.AreEqual(contentType.DefaultTemplate.Alias, result.DefaultTemplate.Alias);
//TODO: Now we need to assert all of the more complicated parts
Assert.AreEqual(contentType.CompositionPropertyGroups.Select(x => x.Name).Distinct().Count(), result.Groups.Count(x => x.Id != -666));
Assert.AreEqual(1, result.Groups.Count(x => x.Id == -666));
Assert.AreEqual(contentType.PropertyGroups.Count(), result.Groups.Count(x => x.Inherited == false && x.Id != -666));
var allPropertiesMapped = result.Groups.SelectMany(x => x.Properties).ToArray();
var allPropertyIdsMapped = allPropertiesMapped.Select(x => x.Id).ToArray();
var allSourcePropertyIds = contentType.CompositionPropertyTypes.Select(x => x.Id).ToArray();
Assert.AreEqual(contentType.PropertyTypes.Count(), allPropertiesMapped.Count(x => x.Inherited == false));
Assert.AreEqual(allPropertyIdsMapped.Count(), allSourcePropertyIds.Count());
Assert.IsTrue(allPropertyIdsMapped.ContainsAll(allSourcePropertyIds));
Assert.AreEqual(1, result.Groups.Count(x => x.ParentTabContentTypes.Any()));
Assert.IsTrue(result.Groups.SelectMany(x => x.ParentTabContentTypes).ContainsAll(new[] {ctMain.Id, ctChild1.Id}));
Assert.AreEqual(contentType.AllowedTemplates.Count(), result.AllowedTemplates.Count());
for (var i = 0; i < contentType.AllowedTemplates.Count(); i++)
{
Assert.AreEqual(contentType.AllowedTemplates.ElementAt(i).Id, result.AllowedTemplates.ElementAt(i).Id);
}
Assert.AreEqual(contentType.AllowedContentTypes.Count(), result.AllowedContentTypes.Count());
for (var i = 0; i < contentType.AllowedContentTypes.Count(); i++)
{
Assert.AreEqual(contentType.AllowedContentTypes.ElementAt(i).Id.Value, result.AllowedContentTypes.ElementAt(i));
}
}
private ContentTypeDisplay CreateSimpleContentTypeDisplay()
{
return new ContentTypeDisplay

View File

@@ -1,4 +1,5 @@
using System;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Models;
@@ -181,11 +182,24 @@ namespace Umbraco.Tests.TestHelpers.Entities
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TinyMCEAlias, DataTypeDatabaseType.Ntext) { Alias = RandomAlias("bodyText", randomizeAliases), Name = "Body Text", Description = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -87 });
contentCollection.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext) { Alias = RandomAlias("author", randomizeAliases) , Name = "Author", Description = "Name of the author", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -88 });
contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = propertyGroupName, SortOrder = 1 });
var pg = new PropertyGroup(contentCollection) {Name = propertyGroupName, SortOrder = 1};
contentType.PropertyGroups.Add(pg);
if (parent != null)
{
var foundPg = parent.PropertyGroups.FirstOrDefault(x => x.Name == propertyGroupName);
if (foundPg != null)
{
//this exists on the parent, so set the parent id
pg.ParentId = foundPg.Id;
}
}
//ensure that nothing is marked as dirty
contentType.ResetDirtyProperties(false);
contentType.SetDefaultTemplate(new Template("Textpage", "textpage"));
return contentType;
}
@@ -399,6 +413,21 @@ namespace Umbraco.Tests.TestHelpers.Entities
return contentType;
}
public static void EnsureAllIds(ContentType contentType, int seedId)
{
//ensure everything has ids
contentType.Id = seedId;
var itemid = seedId + 1;
foreach (var propertyGroup in contentType.PropertyGroups)
{
propertyGroup.Id = itemid++;
}
foreach (var propertyType in contentType.PropertyTypes)
{
propertyType.Id = itemid++;
}
}
private static string RandomAlias(string alias, bool randomizeAliases)
{
if (randomizeAliases)

View File

@@ -126,6 +126,7 @@ namespace Umbraco.Web.Models.Mapping
config.CreateMap<IContentType, ContentTypeDisplay>()
.ForMember(display => display.AllowAsRoot, expression => expression.MapFrom(type => type.AllowedAsRoot))
.ForMember(display => display.ListViewEditorName, expression => expression.Ignore())
//Ignore because this is not actually used for content types
.ForMember(display => display.Trashed, expression => expression.Ignore())

View File

@@ -34,20 +34,28 @@ namespace Umbraco.Web.Models.Mapping
Constants.System.DefaultMembersListViewDataTypeId
};
config.CreateMap<PropertyEditor, DataTypeBasic>();
config.CreateMap<PropertyEditor, DataTypeBasic>()
.ForMember(x => x.IsSystemDataType, expression => expression.Ignore())
.ForMember(x => x.Id, expression => expression.Ignore())
.ForMember(x => x.Trashed, expression => expression.Ignore())
.ForMember(x => x.Key, expression => expression.Ignore())
.ForMember(x => x.ParentId, expression => expression.Ignore())
.ForMember(x => x.Path, expression => expression.Ignore())
.ForMember(x => x.AdditionalData, expression => expression.Ignore());
config.CreateMap<IDataTypeDefinition, DataTypeBasic>()
.ForMember(x => x.Icon, expression => expression.Ignore())
.ForMember(x => x.Alias, expression => expression.Ignore())
.ForMember(x => x.Group, expression => expression.Ignore())
.ForMember(x => x.IsSystemDataType, expression => expression.MapFrom(definition => systemIds.Contains(definition.Id)))
.AfterMap( (def,basic) =>
.AfterMap((def, basic) =>
{
var editor = PropertyEditorResolver.Current.GetByAlias(def.PropertyEditorAlias);
if(editor != null){
if (editor != null)
{
basic.Group = editor.Group;
basic.Icon = editor.Icon;
}
});
config.CreateMap<IDataTypeDefinition, DataTypeDisplay>()
@@ -59,7 +67,17 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(x => x.Notifications, expression => expression.Ignore())
.ForMember(x => x.Icon, expression => expression.Ignore())
.ForMember(x => x.Alias, expression => expression.Ignore())
.ForMember(x => x.IsSystemDataType, expression => expression.MapFrom(definition => systemIds.Contains(definition.Id)));
.ForMember(x => x.Group, expression => expression.Ignore())
.ForMember(x => x.IsSystemDataType, expression => expression.MapFrom(definition => systemIds.Contains(definition.Id)))
.AfterMap((def, basic) =>
{
var editor = PropertyEditorResolver.Current.GetByAlias(def.PropertyEditorAlias);
if (editor != null)
{
basic.Group = editor.Group;
basic.Icon = editor.Icon;
}
});
//gets a list of PreValueFieldDisplay objects from the data type definition
config.CreateMap<IDataTypeDefinition, IEnumerable<PreValueFieldDisplay>>()

View File

@@ -46,6 +46,8 @@ namespace Umbraco.Web.Models.Mapping
//only map id if set to something higher then zero
.ForMember(dto => dto.Id, expression => expression.Condition(display => (Convert.ToInt32(display.Id) > 0)))
.ForMember(dto => dto.Id, expression => expression.MapFrom(display => Convert.ToInt32(display.Id)))
.ForMember(dto => dto.AllowedAsRoot, expression => expression.Ignore())
.ForMember(dto => dto.IsContainer, expression => expression.Ignore())
.ForMember(dto => dto.CreatorId, expression => expression.Ignore())
.ForMember(dto => dto.Level, expression => expression.Ignore())
.ForMember(dto => dto.SortOrder, expression => expression.Ignore())

View File

@@ -1,180 +1,190 @@
using AutoMapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Models.Mapping
{
internal class PropertyTypeGroupResolver : ValueResolver<IContentTypeComposition, IEnumerable<PropertyGroupDisplay>>
{
private readonly ApplicationContext _applicationContext;
private readonly Lazy<PropertyEditorResolver> _propertyEditorResolver;
public PropertyTypeGroupResolver(ApplicationContext applicationContext, Lazy<PropertyEditorResolver> propertyEditorResolver)
{
_applicationContext = applicationContext;
_propertyEditorResolver = propertyEditorResolver;
}
protected override IEnumerable<PropertyGroupDisplay> ResolveCore(IContentTypeComposition source)
{
var groups = new Dictionary<int,PropertyGroupDisplay>();
//for storing generic properties
var genericProperties = new List<PropertyTypeDisplay>();
//iterate through all composite types
foreach (var ct in source.ContentTypeComposition)
{
//process each tab
foreach(var tab in ct.CompositionPropertyGroups){
var group = new PropertyGroupDisplay() { Id = tab.Id, Inherited = true, Name = tab.Name, SortOrder = tab.SortOrder };
group.ContentTypeId = ct.Id;
group.ParentTabContentTypes = new[] { ct.Id };
group.ParentTabContentTypeNames = new[] { ct.Name };
if (tab.ParentId.HasValue)
group.ParentGroupId = tab.ParentId.Value;
group.Properties = MapProperties(tab.PropertyTypes, ct, tab.Id, true);
groups.Add(tab.Id, group);
}
//process inherited generic properties
var inheritedGenProperties = ct.CompositionPropertyTypes.Where(x => x.PropertyGroupId == null);
if (inheritedGenProperties.Any())
genericProperties.AddRange(MapProperties(inheritedGenProperties, ct, 0, true));
}
//pull from own groups
foreach (var ownTab in source.CompositionPropertyGroups)
{
PropertyGroupDisplay group;
//if already added
if (groups.ContainsKey(ownTab.Id))
group = groups[ownTab.Id];
//if parent
else if (ownTab.ParentId.HasValue && groups.ContainsKey(ownTab.ParentId.Value))
group = groups[ownTab.ParentId.Value];
else
{
//if own
group = new PropertyGroupDisplay() { Id = ownTab.Id, Inherited = false, Name = ownTab.Name, SortOrder = ownTab.SortOrder, ContentTypeId = source.Id };
groups.Add(ownTab.Id, group);
}
//merge the properties
var mergedProperties = new List<PropertyTypeDisplay>();
mergedProperties.AddRange(group.Properties);
var newproperties = MapProperties( ownTab.PropertyTypes , source, ownTab.Id, false).Where(x => mergedProperties.Any( y => y.Id == x.Id ) == false);
mergedProperties.AddRange(newproperties);
group.Properties = mergedProperties.OrderBy(x => x.SortOrder);
}
//get all generic properties not already mapped to the generic props collection
var ownGenericProperties = source.CompositionPropertyTypes.Where(x => x.PropertyGroupId == null && !genericProperties.Any(y => y.Id == x.Id));
genericProperties.AddRange(MapProperties(ownGenericProperties, source, 0, false));
if (genericProperties.Any())
{
var genericTab = new PropertyGroupDisplay() { Id = -666, Name = "Generic properties", ParentGroupId = 0, ContentTypeId = source.Id, SortOrder = 999, Inherited = false };
genericTab.Properties = genericProperties;
groups.Add(0, genericTab);
}
//merge tabs based on names (magic and insanity)
var nameGroupedGroups = groups.Values.GroupBy(x => x.Name);
if (nameGroupedGroups.Any(x => x.Count() > 1))
{
var sortedGroups = new List<PropertyGroupDisplay>();
foreach (var groupOfGroups in nameGroupedGroups)
{
//single name groups
if(groupOfGroups.Count() == 1)
sortedGroups.Add(groupOfGroups.First());
else{
//multiple name groups
//find the mother tab - if we have our own use it. otherwise pick a random inherited one - since it wont matter
var mainTab = groupOfGroups.FirstOrDefault(x => x.Inherited == false);
if (mainTab == null)
mainTab = groupOfGroups.First();
//take all properties from all the other tabs and merge into one tab
var properties = new List<PropertyTypeDisplay>();
using AutoMapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Models.Mapping
{
internal class PropertyTypeGroupResolver : ValueResolver<IContentTypeComposition, IEnumerable<PropertyGroupDisplay>>
{
private readonly ApplicationContext _applicationContext;
private readonly Lazy<PropertyEditorResolver> _propertyEditorResolver;
public PropertyTypeGroupResolver(ApplicationContext applicationContext, Lazy<PropertyEditorResolver> propertyEditorResolver)
{
_applicationContext = applicationContext;
_propertyEditorResolver = propertyEditorResolver;
}
/// <summary>
/// Will recursively check all compositions (of compositions) to find the content type that contains the
/// tabId being searched for.
/// </summary>
/// <param name="ct"></param>
/// <param name="tabId"></param>
/// <returns></returns>
private IContentTypeComposition GetContentTypeFromTabId(IContentTypeComposition ct, int tabId)
{
if (ct.PropertyGroups.Any(x => x.Id == tabId)) return ct;
foreach (var composition in ct.ContentTypeComposition)
{
var found = GetContentTypeFromTabId(composition, tabId);
if (found != null) return found;
}
return null;
}
protected override IEnumerable<PropertyGroupDisplay> ResolveCore(IContentTypeComposition source)
{
var groups = new Dictionary<int,PropertyGroupDisplay>();
//for storing generic properties
var genericProperties = new List<PropertyTypeDisplay>();
//add groups directly assigned to this content type
foreach (var tab in source.PropertyGroups)
{
var group = new PropertyGroupDisplay()
{
Id = tab.Id, Inherited = false, Name = tab.Name, SortOrder = tab.SortOrder, ContentTypeId = source.Id
};
group.Properties = MapProperties(tab.PropertyTypes, source, tab.Id, false);
groups.Add(tab.Id, group);
}
//add groups not assigned to this content type (via compositions)
foreach (var tab in source.CompositionPropertyGroups)
{
if (groups.ContainsKey(tab.Id)) continue;
var composition = GetContentTypeFromTabId(source, tab.Id);
if (composition == null)
throw new InvalidOperationException("The tabId " + tab.Id + " was not found on any of the content type's compositions");
var group = new PropertyGroupDisplay()
{
Id = tab.Id, Inherited = false, Name = tab.Name, SortOrder = tab.SortOrder, ContentTypeId = composition.Id,
ParentTabContentTypes = new[] {composition.Id},
ParentTabContentTypeNames = new[] {composition.Name}
};
if (tab.ParentId.HasValue)
group.ParentGroupId = tab.ParentId.Value;
group.Properties = MapProperties(tab.PropertyTypes, composition, tab.Id, true);
groups.Add(tab.Id, group);
}
//process generic properties assigned to this content item (without a group)
//NOTE: -666 is just a thing that is checked for on the front-end... I'm not a fan of this for the mapping
// since this is just for front-end, this could probably be updated to be -666 in the controller which is associated
// with giving the front-end it's data
var entityGenericProperties = source.PropertyTypes.Where(x => x.PropertyGroupId == null);
genericProperties.AddRange(MapProperties(entityGenericProperties, source, -666, false));
//process generic properties from compositions (ensures properties are flagged as inherited)
var currentGenericPropertyIds = genericProperties.Select(x => x.Id).ToArray();
var compositionGenericProperties = source.CompositionPropertyTypes
.Where(x => x.PropertyGroupId == null && currentGenericPropertyIds.Contains(x.Id) == false);
genericProperties.AddRange(MapProperties(compositionGenericProperties, source, -666, true));
//now add the group if there are any generic props
if (genericProperties.Any())
{
var genericTab = new PropertyGroupDisplay
{
Id = -666, Name = "Generic properties", ParentGroupId = 0, ContentTypeId = source.Id, SortOrder = 999, Inherited = false, Properties = genericProperties
};
groups.Add(0, genericTab);
}
//merge tabs based on names (magic and insanity)
var nameGroupedGroups = groups.Values.GroupBy(x => x.Name).ToArray();
if (nameGroupedGroups.Any(x => x.Count() > 1))
{
var sortedGroups = new List<PropertyGroupDisplay>();
foreach (var groupOfGroups in nameGroupedGroups)
{
//single name groups
if (groupOfGroups.Count() == 1)
{
sortedGroups.Add(groupOfGroups.First());
}
else
{
//multiple name groups
//find the mother tab - if we have our own use it. otherwise pick a random inherited one - since it wont matter
var mainTab = groupOfGroups.FirstOrDefault(x => x.Inherited == false) ?? groupOfGroups.First();
//take all properties from all the other tabs and merge into one tab
var properties = new List<PropertyTypeDisplay>();
properties.AddRange(groupOfGroups.Where(x => x.Id != mainTab.Id).SelectMany(x => x.Properties));
properties.AddRange(mainTab.Properties);
mainTab.Properties = properties;
//lock the tab
mainTab.Inherited = true;
properties.AddRange(mainTab.Properties);
mainTab.Properties = properties;
//lock the tab
mainTab.Inherited = true;
//collect all the involved content types
var parents = groupOfGroups.Where(x => x.ContentTypeId != source.Id).ToList();
mainTab.ParentTabContentTypes = parents.Select(x => x.ContentTypeId);
mainTab.ParentTabContentTypeNames = parents.SelectMany(x => x.ParentTabContentTypeNames);
sortedGroups.Add(mainTab);
}
}
return sortedGroups.OrderBy(x => x.SortOrder);
}
return groups.Values.OrderBy(x => x.SortOrder);
}
private IEnumerable<PropertyTypeDisplay> MapProperties(IEnumerable<PropertyType> properties, IContentTypeBase contentType, int groupId, bool inherited)
{
var mappedProperties = new List<PropertyTypeDisplay>();
foreach (var p in properties.Where(x => x.DataTypeDefinitionId != 0) )
{
var editor = _propertyEditorResolver.Value.GetByAlias(p.PropertyEditorAlias);
var preVals = _applicationContext.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(p.DataTypeDefinitionId);
mappedProperties.Add(
new PropertyTypeDisplay()
{
Id = p.Id,
Alias = p.Alias,
Description = p.Description,
Editor = p.PropertyEditorAlias,
Validation = new PropertyTypeValidation() { Mandatory = p.Mandatory, Pattern = p.ValidationRegExp },
Label = p.Name,
View = editor.ValueEditor.View,
Config = editor.PreValueEditor.ConvertDbToEditor(editor.DefaultPreValues, preVals) ,
Value = "",
ContentTypeId = contentType.Id,
ContentTypeName = contentType.Name,
GroupId = groupId,
Inherited = inherited,
DataTypeId = p.DataTypeDefinitionId,
SortOrder = p.SortOrder
});
}
return mappedProperties;
}
}
}
mainTab.ParentTabContentTypes = parents.SelectMany(x => x.ParentTabContentTypes).ToArray();
mainTab.ParentTabContentTypeNames = parents.SelectMany(x => x.ParentTabContentTypeNames).ToArray();
sortedGroups.Add(mainTab);
}
}
return sortedGroups.OrderBy(x => x.SortOrder);
}
return groups.Values.OrderBy(x => x.SortOrder);
}
private IEnumerable<PropertyTypeDisplay> MapProperties(IEnumerable<PropertyType> properties, IContentTypeBase contentType, int groupId, bool inherited)
{
var mappedProperties = new List<PropertyTypeDisplay>();
foreach (var p in properties.Where(x => x.DataTypeDefinitionId != 0) )
{
var editor = _propertyEditorResolver.Value.GetByAlias(p.PropertyEditorAlias);
var preVals = _applicationContext.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(p.DataTypeDefinitionId);
mappedProperties.Add(
new PropertyTypeDisplay()
{
Id = p.Id,
Alias = p.Alias,
Description = p.Description,
Editor = p.PropertyEditorAlias,
Validation = new PropertyTypeValidation() { Mandatory = p.Mandatory, Pattern = p.ValidationRegExp },
Label = p.Name,
View = editor.ValueEditor.View,
Config = editor.PreValueEditor.ConvertDbToEditor(editor.DefaultPreValues, preVals) ,
Value = "",
ContentTypeId = contentType.Id,
ContentTypeName = contentType.Name,
GroupId = groupId,
Inherited = inherited,
DataTypeId = p.DataTypeDefinitionId,
SortOrder = p.SortOrder
});
}
return mappedProperties;
}
}
}