Adding test fixture for the EntityService.

Refactoring sql query used in the EntityRepository.
Correcting a few issues with the UmbracoObjectTypes extensions.
This commit is contained in:
Morten Christensen
2013-03-30 09:52:37 -01:00
parent 72ad9f51c1
commit 3f7a93e0c9
10 changed files with 280 additions and 52 deletions

View File

@@ -5,6 +5,7 @@ namespace Umbraco.Core.CodeAnnotations
/// <summary>
/// Attribute to add a Friendly Name string with an UmbracoObjectType enum value
/// </summary>
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)]
internal class FriendlyNameAttribute : Attribute
{
/// <summary>

View File

@@ -5,6 +5,7 @@ namespace Umbraco.Core.CodeAnnotations
/// <summary>
/// Attribute to associate a GUID string and Type with an UmbracoObjectType Enum value
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
internal class UmbracoObjectTypeAttribute : Attribute
{
public UmbracoObjectTypeAttribute(string objectId)

View File

@@ -18,6 +18,8 @@ namespace Umbraco.Core.Models
private bool _trashed;
private bool _hasChildren;
private bool _isPublished;
private bool _isDraft;
private bool _hasPendingChanges;
private Guid _nodeObjectTypeId;
private static readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, int>(x => x.CreatorId);
@@ -29,6 +31,8 @@ namespace Umbraco.Core.Models
private static readonly PropertyInfo TrashedSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, bool>(x => x.Trashed);
private static readonly PropertyInfo HasChildrenSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, bool>(x => x.HasChildren);
private static readonly PropertyInfo IsPublishedSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, bool>(x => x.IsPublished);
private static readonly PropertyInfo IsDraftSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, bool>(x => x.IsDraft);
private static readonly PropertyInfo HasPendingChangesSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, bool>(x => x.HasPendingChanges);
private static readonly PropertyInfo NodeObjectTypeIdSelector = ExpressionHelper.GetPropertyInfo<UmbracoEntity, Guid>(x => x.NodeObjectTypeId);
public UmbracoEntity()
@@ -157,6 +161,32 @@ namespace Umbraco.Core.Models
}
}
public bool IsDraft
{
get { return _isPublished; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
_isDraft = value;
return _isDraft;
}, _isDraft, IsDraftSelector);
}
}
public bool HasPendingChanges
{
get { return _hasPendingChanges; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
_hasPendingChanges = value;
return _hasPendingChanges;
}, _hasPendingChanges, HasPendingChangesSelector);
}
}
public Guid NodeObjectTypeId
{
get { return _nodeObjectTypeId; }

View File

@@ -51,7 +51,15 @@ namespace Umbraco.Core.Models
if (UmbracoObjectTypeCache.ContainsKey(umbracoObjectType))
return UmbracoObjectTypeCache[umbracoObjectType];
var attribute = umbracoObjectType.GetType().FirstAttribute<UmbracoObjectTypeAttribute>();
var type = typeof(UmbracoObjectTypes);
var memInfo = type.GetMember(umbracoObjectType.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
false);
if (attributes.Length == 0)
return Guid.Empty;
var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
if (attribute == null)
return Guid.Empty;
@@ -77,7 +85,15 @@ namespace Umbraco.Core.Models
/// <returns>a string of the FriendlyName</returns>
public static string GetFriendlyName(this UmbracoObjectTypes umbracoObjectType)
{
var attribute = umbracoObjectType.GetType().FirstAttribute<FriendlyNameAttribute>();
var type = typeof(UmbracoObjectTypes);
var memInfo = type.GetMember(umbracoObjectType.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
false);
if (attributes.Length == 0)
return string.Empty;
var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
if (attribute == null)
return string.Empty;

View File

@@ -1,4 +1,5 @@
using System.Globalization;
using System;
using System.Globalization;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Repositories;
@@ -20,9 +21,16 @@ namespace Umbraco.Core.Persistence.Factories
ParentId = dto.ParentId,
Path = dto.Path,
SortOrder = dto.SortOrder,
HasChildren = dto.Children > 0,
IsPublished = dto.HasPublishedVersion.HasValue && dto.HasPublishedVersion.Value > 0
HasChildren = dto.Children > 0
};
entity.IsPublished = dto.PublishedVersion != default(Guid) ||
(dto.NewestVersion != default(Guid) && dto.PublishedVersion == dto.NewestVersion);
entity.IsDraft = dto.NewestVersion != default(Guid) &&
(dto.PublishedVersion == default(Guid) || dto.PublishedVersion != dto.NewestVersion);
entity.HasPendingChanges = dto.PublishedVersion != default(Guid) && dto.NewestVersion != default(Guid) &&
dto.PublishedVersion != dto.NewestVersion;
return entity;
}

View File

@@ -59,7 +59,7 @@ namespace Umbraco.Core.Persistence.Repositories
public virtual IUmbracoEntity Get(int id, Guid objectTypeId)
{
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
var sql = GetBaseWhere(GetBase, isContent, objectTypeId, id).Append(GetGroupBy());
var sql = GetBaseWhere(GetBase, isContent, objectTypeId, id).Append(GetGroupBy(isContent));
var nodeDto = _work.Database.FirstOrDefault<UmbracoEntityDto>(sql);
if (nodeDto == null)
return null;
@@ -82,7 +82,7 @@ namespace Umbraco.Core.Persistence.Repositories
else
{
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
var sql = GetBaseWhere(GetBase, isContent, objectTypeId).Append(GetGroupBy());
var sql = GetBaseWhere(GetBase, isContent, objectTypeId).Append(GetGroupBy(isContent));
var dtos = _work.Database.Fetch<UmbracoEntityDto>(sql);
var factory = new UmbracoEntityFactory();
@@ -99,7 +99,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sqlClause = GetBase(false);
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
var sql = translator.Translate().Append(GetGroupBy());
var sql = translator.Translate().Append(GetGroupBy(false));
var dtos = _work.Database.Fetch<UmbracoEntityDto>(sql);
@@ -114,7 +114,7 @@ namespace Umbraco.Core.Persistence.Repositories
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
var sqlClause = GetBaseWhere(GetBase, isContent, objectTypeId);
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
var sql = translator.Translate().Append(GetGroupBy());
var sql = translator.Translate().Append(GetGroupBy(isContent));
var dtos = _work.Database.Fetch<UmbracoEntityDto>(sql);
@@ -143,22 +143,26 @@ namespace Umbraco.Core.Persistence.Repositories
"main.text",
"main.nodeObjectType",
"main.createDate",
"COUNT(parent.parentID) as children",
isContent
? "SUM(CONVERT(int, document.published)) as published"
: "SUM(0) as published"
"COUNT(parent.parentID) as children"
};
if (isContent)
{
columns.Add("published.versionId as publishedVerison");
columns.Add("latest.versionId as newestVersion");
}
var sql = new Sql()
.Select(columns.ToArray())
.From("FROM umbracoNode main")
.From("umbracoNode main")
.LeftJoin("umbracoNode parent").On("parent.parentID = main.id");
//NOTE Should this account for newest = 1 ? Scenarios: unsaved, saved not published, published
if (isContent)
sql.LeftJoin("cmsDocument document").On("document.nodeId = main.id");
{
sql.LeftJoin("(SELECT nodeId, versionId FROM cmsDocument WHERE published = 1 GROUP BY nodeId, versionId) as published").On("main.id = published.nodeId");
sql.LeftJoin("(SELECT nodeId, versionId FROM cmsDocument WHERE newest = 1 GROUP BY nodeId, versionId) as latest").On("main.id = latest.nodeId");
}
return sql;
}
@@ -174,7 +178,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = baseQuery(isContent)
.Where("main.id = @Id", new {Id = id})
.Append(GetGroupBy());
.Append(GetGroupBy(isContent));
return sql;
}
@@ -186,12 +190,31 @@ namespace Umbraco.Core.Persistence.Repositories
return sql;
}
protected virtual Sql GetGroupBy()
protected virtual Sql GetGroupBy(bool isContent)
{
var columns = new List<object>
{
"main.id",
"main.trashed",
"main.parentID",
"main.nodeUser",
"main.level",
"main.path",
"main.sortOrder",
"main.uniqueID",
"main.text",
"main.nodeObjectType",
"main.createDate"
};
if (isContent)
{
columns.Add("published.versionId");
columns.Add("latest.versionId");
}
var sql = new Sql()
.GroupBy("main.id", "main.trashed", "main.parentID", "main.nodeUser", "main.level",
"main.path", "main.sortOrder", "main.uniqueID", "main.text",
"main.nodeObjectType", "main.createDate")
.GroupBy(columns.ToArray())
.OrderBy("main.sortOrder");
return sql;
}
@@ -218,8 +241,11 @@ namespace Umbraco.Core.Persistence.Repositories
[Column("children")]
public int Children { get; set; }
[Column("published")]
public int? HasPublishedVersion { get; set; }
[Column("publishedVerison")]
public Guid PublishedVersion { get; set; }
[Column("newestVerison")]
public Guid NewestVersion { get; set; }
}
#endregion
}

View File

@@ -14,20 +14,29 @@ namespace Umbraco.Core.Services
{
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
private readonly RepositoryFactory _repositoryFactory;
private readonly Dictionary<string, UmbracoObjectTypes> _supportedObjectTypes = new Dictionary<string, UmbracoObjectTypes>
{
{typeof(IDataTypeDefinition).FullName, UmbracoObjectTypes.DataType},
{typeof(IContent).FullName, UmbracoObjectTypes.Document},
{typeof(IContentType).FullName, UmbracoObjectTypes.DocumentType},
{typeof(IMedia).FullName, UmbracoObjectTypes.Media},
{typeof(IMediaType).FullName, UmbracoObjectTypes.MediaType},
{typeof(ITemplate).FullName, UmbracoObjectTypes.Template}
};
private readonly IContentService _contentService;
private readonly IContentTypeService _contentTypeService;
private readonly IMediaService _mediaService;
private readonly IDataTypeService _dataTypeService;
private readonly Dictionary<string, Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>> _supportedObjectTypes;
public EntityService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory)
public EntityService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, IContentService contentService, IContentTypeService contentTypeService, IMediaService mediaService, IDataTypeService dataTypeService)
{
_uowProvider = provider;
_repositoryFactory = repositoryFactory;
_contentService = contentService;
_contentTypeService = contentTypeService;
_mediaService = mediaService;
_dataTypeService = dataTypeService;
_supportedObjectTypes = new Dictionary<string, Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>>
{
{typeof(IDataTypeDefinition).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.DataType, _dataTypeService.GetDataTypeDefinitionById)},
{typeof(IContent).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.Document, _contentService.GetById)},
{typeof(IContentType).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.DocumentType, _contentTypeService.GetContentType)},
{typeof(IMedia).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.Media, _mediaService.GetById)},
{typeof(IMediaType).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.MediaType, _contentTypeService.GetMediaType)}
};
}
/// <summary>
@@ -50,9 +59,11 @@ namespace Umbraco.Core.Services
}
var objectType = GetObjectType(id);
var entityType = GetEntityType(objectType);
var typeFullName = entityType.FullName;
var entity = _supportedObjectTypes[typeFullName].Item2(id);
//TODO Implementing loading from the various services
throw new NotImplementedException();
return entity;
}
/// <summary>
@@ -76,9 +87,11 @@ namespace Umbraco.Core.Services
}
}
//TODO Implementing loading from the various services
throw new NotImplementedException();
var entityType = GetEntityType(umbracoObjectType);
var typeFullName = entityType.FullName;
var entity = _supportedObjectTypes[typeFullName].Item2(id);
return entity;
}
/// <summary>
@@ -101,16 +114,15 @@ namespace Umbraco.Core.Services
}
}
Mandate.That<NotSupportedException>(_supportedObjectTypes.ContainsKey(typeof (T).FullName), () =>
{
throw
new NotSupportedException
("The passed in type is not supported");
});
var objectType = _supportedObjectTypes[typeof(T).FullName];
var typeFullName = typeof(T).FullName;
Mandate.That<NotSupportedException>(_supportedObjectTypes.ContainsKey(typeFullName), () =>
{
throw new NotSupportedException
("The passed in type is not supported");
});
var entity = _supportedObjectTypes[typeFullName].Item2(id);
//TODO Implementing loading from the various services
throw new NotImplementedException();
return entity;
}
/// <summary>
@@ -243,12 +255,13 @@ namespace Umbraco.Core.Services
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/> objects</returns>
public virtual IEnumerable<IUmbracoEntity> GetAll<T>() where T : IUmbracoEntity
{
Mandate.That<NotSupportedException>(_supportedObjectTypes.ContainsKey(typeof(T).FullName), () =>
var typeFullName = typeof (T).FullName;
Mandate.That<NotSupportedException>(_supportedObjectTypes.ContainsKey(typeFullName), () =>
{
throw new NotSupportedException
("The passed in type is not supported");
});
var objectType = _supportedObjectTypes[typeof (T).FullName];
var objectType = _supportedObjectTypes[typeFullName].Item1;
return GetAll(objectType);
}
@@ -260,6 +273,14 @@ namespace Umbraco.Core.Services
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/> objects</returns>
public virtual IEnumerable<IUmbracoEntity> GetAll(UmbracoObjectTypes umbracoObjectType)
{
var entityType = GetEntityType(umbracoObjectType);
var typeFullName = entityType.FullName;
Mandate.That<NotSupportedException>(_supportedObjectTypes.ContainsKey(typeFullName), () =>
{
throw new NotSupportedException
("The passed in type is not supported");
});
var objectTypeId = umbracoObjectType.GetGuid();
using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork()))
{
@@ -274,6 +295,15 @@ namespace Umbraco.Core.Services
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/> objects</returns>
public virtual IEnumerable<IUmbracoEntity> GetAll(Guid objectTypeId)
{
var umbracoObjectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(objectTypeId);
var entityType = GetEntityType(umbracoObjectType);
var typeFullName = entityType.FullName;
Mandate.That<NotSupportedException>(_supportedObjectTypes.ContainsKey(typeFullName), () =>
{
throw new NotSupportedException
("The passed in type is not supported");
});
using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork()))
{
return repository.GetAll(objectTypeId);
@@ -328,7 +358,12 @@ namespace Umbraco.Core.Services
/// <returns>Type of the entity</returns>
public virtual Type GetEntityType(UmbracoObjectTypes umbracoObjectType)
{
var attribute = umbracoObjectType.GetType().FirstAttribute<UmbracoObjectTypeAttribute>();
var type = typeof(UmbracoObjectTypes);
var memInfo = type.GetMember(umbracoObjectType.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
false);
var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
if (attribute == null)
throw new NullReferenceException("The passed in UmbracoObjectType does not contain an UmbracoObjectTypeAttribute, which is used to retrieve the Type.");

View File

@@ -79,8 +79,9 @@ namespace Umbraco.Core.Services
if(_packagingService == null)
_packagingService = new Lazy<PackagingService>(() => new PackagingService(_contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value, _fileService.Value, repositoryFactory.Value, provider));
if (_entityService == null)
_entityService = new Lazy<EntityService>(() => new EntityService(provider, repositoryFactory.Value));
_entityService = new Lazy<EntityService>(() => new EntityService(provider, repositoryFactory.Value, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value));
}
/// <summary>

View File

@@ -0,0 +1,109 @@
using System;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models;
namespace Umbraco.Tests.Services
{
/// <summary>
/// Tests covering the EntityService
/// </summary>
[TestFixture, RequiresSTA]
public class EntityServiceTests : BaseServiceTest
{
[SetUp]
public override void Initialize()
{
base.Initialize();
}
[TearDown]
public override void TearDown()
{
base.TearDown();
}
[Test]
public void EntityService_Can_Find_All_Content_By_UmbracoObjectTypes()
{
var service = ServiceContext.EntityService;
var entities = service.GetAll(UmbracoObjectTypes.Document);
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(4));
Assert.That(entities.Any(x => x.Trashed), Is.True);
}
[Test]
public void EntityService_Can_Find_All_Content_By_UmbracoObjectType_Id()
{
var service = ServiceContext.EntityService;
var objectTypeId = new Guid(Constants.ObjectTypes.Document);
var entities = service.GetAll(objectTypeId);
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(4));
Assert.That(entities.Any(x => x.Trashed), Is.True);
}
[Test]
public void EntityService_Can_Find_All_Content_By_Type()
{
var service = ServiceContext.EntityService;
var entities = service.GetAll<IContent>();
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(4));
Assert.That(entities.Any(x => x.Trashed), Is.True);
}
[Test]
public void EntityService_Throws_When_Getting_All_With_Invalid_Type()
{
var service = ServiceContext.EntityService;
var objectTypeId = new Guid(Constants.ObjectTypes.ContentItem);
Assert.Throws<NotSupportedException>(() => service.GetAll<IContentBase>());
Assert.Throws<NullReferenceException>(() => service.GetAll(UmbracoObjectTypes.ContentItem));
Assert.Throws<NullReferenceException>(() => service.GetAll(objectTypeId));
}
[Test]
public void EntityService_Can_Find_All_ContentTypes_By_UmbracoObjectTypes()
{
var service = ServiceContext.EntityService;
var entities = service.GetAll(UmbracoObjectTypes.DocumentType);
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(1));
}
[Test]
public void EntityService_Can_Find_All_ContentTypes_By_UmbracoObjectType_Id()
{
var service = ServiceContext.EntityService;
var objectTypeId = new Guid(Constants.ObjectTypes.DocumentType);
var entities = service.GetAll(objectTypeId);
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(1));
}
[Test]
public void EntityService_Can_Find_All_ContentTypes_By_Type()
{
var service = ServiceContext.EntityService;
var entities = service.GetAll<IContentType>();
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(1));
}
}
}

View File

@@ -295,6 +295,7 @@
<Compile Include="Services\ContentServiceTests.cs" />
<Compile Include="Services\ContentTypeServiceTests.cs" />
<Compile Include="Services\DataTypeServiceTests.cs" />
<Compile Include="Services\EntityServiceTests.cs" />
<Compile Include="Services\Importing\PackageImportTests.cs" />
<Compile Include="Services\Importing\ImportResources.Designer.cs">
<AutoGen>True</AutoGen>