Merge remote-tracking branch 'origin/netcore/dev' into netcore/feature/AB5822-smidge-implementation
# Conflicts: # src/Umbraco.Infrastructure/Media/ImageProcessorImageUrlGenerator.cs # src/Umbraco.Infrastructure/Models/ImageProcessorImageUrlGenerator.cs # src/Umbraco.Infrastructure/Models/ImageSharpImageUrlGenerator.cs # src/Umbraco.Web.BackOffice/AspNetCore/AspNetCoreComposer.cs # src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs # src/Umbraco.Web.UI.NetCore/Startup.cs # src/Umbraco.Web.UI.NetCore/appsettings.json
This commit is contained in:
@@ -12,7 +12,7 @@ namespace Umbraco.Configuration.Models
|
||||
/// </summary>
|
||||
internal class GlobalSettings : IGlobalSettings
|
||||
{
|
||||
private const string Prefix = Constants.Configuration.ConfigPrefix + "Global:";
|
||||
public const string Prefix = Constants.Configuration.ConfigPrefix + "Global:";
|
||||
|
||||
internal const string
|
||||
StaticReservedPaths = "~/app_plugins/,~/install/,~/mini-profiler-resources/,"; //must end with a comma!
|
||||
|
||||
@@ -32,4 +32,10 @@
|
||||
<ProjectReference Include="..\Umbraco.Core\Umbraco.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Umbraco.Tests.Integration</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -18,11 +18,13 @@ namespace Umbraco.Core.Composing
|
||||
{
|
||||
"Umbraco.Core",
|
||||
"Umbraco.Web",
|
||||
"Umbraco.Web.BackOffice",
|
||||
"Umbraco.Infrastructure",
|
||||
"Umbraco.PublishedCache.NuCache",
|
||||
"Umbraco.ModelsBuilder.Embedded",
|
||||
"Umbraco.Examine.Lucene",
|
||||
"Umbraco.Web.Common",
|
||||
"Umbraco.Web.BackOffice",
|
||||
"Umbraco.Web.Website",
|
||||
};
|
||||
|
||||
public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly)
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Configuration
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
|
||||
public UmbracoVersion(IGlobalSettings globalSettings)
|
||||
:this()
|
||||
: this()
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ namespace Umbraco.Core.Configuration
|
||||
/// <para>Is the one that the CLR checks for compatibility. Therefore, it changes only on
|
||||
/// hard-breaking changes (for instance, on new major versions).</para>
|
||||
/// </remarks>
|
||||
public Version AssemblyVersion {get; }
|
||||
public Version AssemblyVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assembly file version of the Umbraco code.
|
||||
@@ -83,11 +83,13 @@ namespace Umbraco.Core.Configuration
|
||||
/// and changes during an upgrade. The executing code version changes when new code is
|
||||
/// deployed. The site/files version changes during an upgrade.</para>
|
||||
/// </remarks>
|
||||
public SemVersion LocalVersion {
|
||||
public SemVersion LocalVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = _globalSettings.ConfigurationStatus;
|
||||
return value.IsNullOrWhiteSpace() ? null : SemVersion.TryParse(value, out var semver) ? semver : null;
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Umbraco.Core.Models
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Media
|
||||
{
|
||||
public interface IImageUrlGenerator
|
||||
{
|
||||
12
src/Umbraco.Core/Models/IKeyValue.cs
Normal file
12
src/Umbraco.Core/Models/IKeyValue.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IKeyValue : IEntity
|
||||
{
|
||||
string Identifier { get; set; }
|
||||
|
||||
string Value { get; set; }
|
||||
}
|
||||
}
|
||||
33
src/Umbraco.Core/Models/KeyValue.cs
Normal file
33
src/Umbraco.Core/Models/KeyValue.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements <see cref="IKeyValue"/>.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class KeyValue : EntityBase, IKeyValue, IEntity
|
||||
{
|
||||
private string _identifier;
|
||||
private string _value;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Identifier
|
||||
{
|
||||
get => _identifier;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _identifier, nameof(Identifier));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Value
|
||||
{
|
||||
get => _value;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _value, nameof(Value));
|
||||
}
|
||||
|
||||
bool IEntity.HasIdentity => !string.IsNullOrEmpty(Identifier);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Net
|
||||
{
|
||||
// TODO: This shouldn't be in this namespace?
|
||||
@@ -11,5 +13,13 @@ namespace Umbraco.Net
|
||||
/// Terminates the current application. The application restarts the next time a request is received for it.
|
||||
/// </summary>
|
||||
void Restart();
|
||||
|
||||
event EventHandler ApplicationInit;
|
||||
}
|
||||
|
||||
|
||||
public interface IUmbracoApplicationLifetimeManager
|
||||
{
|
||||
void InvokeApplicationInit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IKeyValueRepository : IReadRepository<string, IKeyValue>, IWriteRepository<IKeyValue>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,24 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </summary>
|
||||
/// <param name="username"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("This method will be removed in future versions. Please use ExistsByUserName instead.")]
|
||||
bool Exists(string username);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a user with the username exists
|
||||
/// </summary>
|
||||
/// <param name="username"></param>
|
||||
/// <returns></returns>
|
||||
bool ExistsByUserName(string username);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a user with the login exists
|
||||
/// </summary>
|
||||
/// <param name="username"></param>
|
||||
/// <returns></returns>
|
||||
bool ExistsByLogin(string login);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IUser"/> objects associated with a given group
|
||||
/// </summary>
|
||||
|
||||
@@ -32,12 +32,13 @@ namespace Umbraco.Core.Services
|
||||
private readonly Lazy<IExternalLoginService> _externalLoginService;
|
||||
private readonly Lazy<IRedirectUrlService> _redirectUrlService;
|
||||
private readonly Lazy<IConsentService> _consentService;
|
||||
private readonly Lazy<IKeyValueService> _keyValueService;
|
||||
private readonly Lazy<IContentTypeBaseServiceProvider> _contentTypeBaseServiceProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServiceContext"/> class with lazy services.
|
||||
/// </summary>
|
||||
public ServiceContext(Lazy<IPublicAccessService> publicAccessService, Lazy<IDomainService> domainService, Lazy<IAuditService> auditService, Lazy<ILocalizedTextService> localizedTextService, Lazy<ITagService> tagService, Lazy<IContentService> contentService, Lazy<IUserService> userService, Lazy<IMemberService> memberService, Lazy<IMediaService> mediaService, Lazy<IContentTypeService> contentTypeService, Lazy<IMediaTypeService> mediaTypeService, Lazy<IDataTypeService> dataTypeService, Lazy<IFileService> fileService, Lazy<ILocalizationService> localizationService, Lazy<IPackagingService> packagingService, Lazy<IServerRegistrationService> serverRegistrationService, Lazy<IEntityService> entityService, Lazy<IRelationService> relationService, Lazy<IMacroService> macroService, Lazy<IMemberTypeService> memberTypeService, Lazy<IMemberGroupService> memberGroupService, Lazy<INotificationService> notificationService, Lazy<IExternalLoginService> externalLoginService, Lazy<IRedirectUrlService> redirectUrlService, Lazy<IConsentService> consentService, Lazy<IContentTypeBaseServiceProvider> contentTypeBaseServiceProvider)
|
||||
public ServiceContext(Lazy<IPublicAccessService> publicAccessService, Lazy<IDomainService> domainService, Lazy<IAuditService> auditService, Lazy<ILocalizedTextService> localizedTextService, Lazy<ITagService> tagService, Lazy<IContentService> contentService, Lazy<IUserService> userService, Lazy<IMemberService> memberService, Lazy<IMediaService> mediaService, Lazy<IContentTypeService> contentTypeService, Lazy<IMediaTypeService> mediaTypeService, Lazy<IDataTypeService> dataTypeService, Lazy<IFileService> fileService, Lazy<ILocalizationService> localizationService, Lazy<IPackagingService> packagingService, Lazy<IServerRegistrationService> serverRegistrationService, Lazy<IEntityService> entityService, Lazy<IRelationService> relationService, Lazy<IMacroService> macroService, Lazy<IMemberTypeService> memberTypeService, Lazy<IMemberGroupService> memberGroupService, Lazy<INotificationService> notificationService, Lazy<IExternalLoginService> externalLoginService, Lazy<IRedirectUrlService> redirectUrlService, Lazy<IConsentService> consentService, Lazy<IKeyValueService> keyValueService, Lazy<IContentTypeBaseServiceProvider> contentTypeBaseServiceProvider)
|
||||
{
|
||||
_publicAccessService = publicAccessService;
|
||||
_domainService = domainService;
|
||||
@@ -64,6 +65,7 @@ namespace Umbraco.Core.Services
|
||||
_externalLoginService = externalLoginService;
|
||||
_redirectUrlService = redirectUrlService;
|
||||
_consentService = consentService;
|
||||
_keyValueService = keyValueService;
|
||||
_contentTypeBaseServiceProvider = contentTypeBaseServiceProvider;
|
||||
}
|
||||
|
||||
@@ -99,6 +101,7 @@ namespace Umbraco.Core.Services
|
||||
IServerRegistrationService serverRegistrationService = null,
|
||||
IRedirectUrlService redirectUrlService = null,
|
||||
IConsentService consentService = null,
|
||||
IKeyValueService keyValueService = null,
|
||||
IContentTypeBaseServiceProvider contentTypeBaseServiceProvider = null)
|
||||
{
|
||||
Lazy<T> Lazy<T>(T service) => service == null ? null : new Lazy<T>(() => service);
|
||||
@@ -129,6 +132,7 @@ namespace Umbraco.Core.Services
|
||||
Lazy(externalLoginService),
|
||||
Lazy(redirectUrlService),
|
||||
Lazy(consentService),
|
||||
Lazy(keyValueService),
|
||||
Lazy(contentTypeBaseServiceProvider)
|
||||
);
|
||||
}
|
||||
@@ -258,6 +262,11 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public IConsentService ConsentService => _consentService.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the KeyValueService.
|
||||
/// </summary>
|
||||
public IKeyValueService KeyValueService => _keyValueService.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ContentTypeServiceBaseFactory.
|
||||
/// </summary>
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool IsClientSideRequest(this Uri url)
|
||||
public static bool IsClientSideRequest(this Uri url)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions
|
||||
composition.RegisterUnique<IScriptRepository, ScriptRepository>();
|
||||
composition.RegisterUnique<IStylesheetRepository, StylesheetRepository>();
|
||||
composition.RegisterUnique<IContentTypeCommonRepository, ContentTypeCommonRepository>();
|
||||
composition.RegisterUnique<IKeyValueRepository, KeyValueRepository>();
|
||||
composition.RegisterUnique<IInstallationRepository, InstallationRepository>();
|
||||
composition.RegisterUnique<IUpgradeCheckRepository, UpgradeCheckRepository>();
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Media;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Web.Models
|
||||
namespace Umbraco.Infrastructure.Media
|
||||
{
|
||||
public class ImageSharpImageUrlGenerator : IImageUrlGenerator
|
||||
{
|
||||
@@ -344,7 +344,7 @@ namespace Umbraco.Core.Migrations.Install
|
||||
var stateValueKey = upgrader.StateValueKey;
|
||||
var finalState = upgrader.Plan.FinalState;
|
||||
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.KeyValue, "key", false, new KeyValueDto { Key = stateValueKey, Value = finalState, Updated = DateTime.Now });
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.KeyValue, "key", false, new KeyValueDto { Key = stateValueKey, Value = finalState, UpdateDate = DateTime.Now });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Umbraco.Core.Migrations.Install
|
||||
|
||||
if (e.Cancel == false)
|
||||
{
|
||||
var dataCreation = new DatabaseDataCreator(_database, _logger,_umbracoVersion, _globalSettings);
|
||||
var dataCreation = new DatabaseDataCreator(_database, _logger, _umbracoVersion, _globalSettings);
|
||||
foreach (var table in OrderedTables)
|
||||
CreateTable(false, table, dataCreation);
|
||||
}
|
||||
@@ -451,7 +451,7 @@ namespace Umbraco.Core.Migrations.Install
|
||||
|
||||
//Execute the Create Table sql
|
||||
var created = _database.Execute(new Sql(createSql));
|
||||
_logger.Info<DatabaseSchemaCreator>("Create Table {TableName} ({Created}): \n {Sql}", tableName, created, createSql);
|
||||
_logger.Info<DatabaseSchemaCreator>("Create Table {TableName} ({Created}): \n {Sql}", tableName, created, createSql);
|
||||
|
||||
//If any statements exists for the primary key execute them here
|
||||
if (string.IsNullOrEmpty(createPrimaryKeySql) == false)
|
||||
@@ -487,11 +487,11 @@ namespace Umbraco.Core.Migrations.Install
|
||||
|
||||
if (overwrite)
|
||||
{
|
||||
_logger.Info<Database>("Table {TableName} was recreated", tableName);
|
||||
_logger.Info<Database>("Table {TableName} was recreated", tableName);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Info<Database>("New table {TableName} was created", tableName);
|
||||
_logger.Info<Database>("New table {TableName} was created", tableName);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Strings;
|
||||
using Umbraco.Web.Actions;
|
||||
using Umbraco.Web.Services;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
|
||||
@@ -21,6 +21,6 @@ namespace Umbraco.Core.Persistence.Dtos
|
||||
|
||||
[Column("updated")]
|
||||
[Constraint(Default = SystemMethods.CurrentDateTime)]
|
||||
public DateTime Updated { get; set; }
|
||||
public DateTime UpdateDate { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,6 +253,11 @@ namespace Umbraco.Core.Persistence
|
||||
_masterCstr = $@"Server=(localdb)\{instanceName};Integrated Security=True;";
|
||||
}
|
||||
|
||||
public static string GetConnectionString(string instanceName, string databaseName)
|
||||
{
|
||||
return $@"Server=(localdb)\{instanceName};Integrated Security=True;Database={databaseName};";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a LocalDb connection string.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NPoco;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
internal class KeyValueRepository : NPocoRepositoryBase<string, IKeyValue>, IKeyValueRepository
|
||||
{
|
||||
public KeyValueRepository(IScopeAccessor scopeAccessor, ILogger logger)
|
||||
: base(scopeAccessor, AppCaches.NoCache, logger)
|
||||
{ }
|
||||
|
||||
#region Overrides of IReadWriteQueryRepository<string, IKeyValue>
|
||||
|
||||
public override void Save(IKeyValue entity)
|
||||
{
|
||||
if (Get(entity.Identifier) == null)
|
||||
PersistNewItem(entity);
|
||||
else
|
||||
PersistUpdatedItem(entity);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides of NPocoRepositoryBase<string, IKeyValue>
|
||||
|
||||
protected override Guid NodeObjectTypeId => throw new NotSupportedException();
|
||||
|
||||
protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
|
||||
{
|
||||
var sql = SqlContext.Sql();
|
||||
|
||||
sql = isCount
|
||||
? sql.SelectCount()
|
||||
: sql.Select<KeyValueDto>();
|
||||
|
||||
sql
|
||||
.From<KeyValueDto>();
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
{
|
||||
return Constants.DatabaseSchema.Tables.KeyValue + ".key = @id";
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> GetDeleteClauses()
|
||||
{
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
protected override IKeyValue PerformGet(string id)
|
||||
{
|
||||
var sql = GetBaseQuery(false).Where<KeyValueDto>(x => x.Key == id);
|
||||
var dto = Database.Fetch<KeyValueDto>(sql).FirstOrDefault();
|
||||
return dto == null ? null : Map(dto);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IKeyValue> PerformGetAll(params string[] ids)
|
||||
{
|
||||
var sql = GetBaseQuery(false).WhereIn<KeyValueDto>(x => x.Key, ids);
|
||||
var dtos = Database.Fetch<KeyValueDto>(sql);
|
||||
return dtos.WhereNotNull().Select(Map);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IKeyValue> PerformGetByQuery(IQuery<IKeyValue> query)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
protected override void PersistNewItem(IKeyValue entity)
|
||||
{
|
||||
var dto = Map(entity);
|
||||
Database.Insert(dto);
|
||||
}
|
||||
|
||||
protected override void PersistUpdatedItem(IKeyValue entity)
|
||||
{
|
||||
var dto = Map(entity);
|
||||
Database.Update(dto);
|
||||
}
|
||||
|
||||
private static KeyValueDto Map(IKeyValue keyValue)
|
||||
{
|
||||
if (keyValue == null) return null;
|
||||
|
||||
return new KeyValueDto
|
||||
{
|
||||
Key = keyValue.Identifier,
|
||||
Value = keyValue.Value,
|
||||
UpdateDate = keyValue.UpdateDate,
|
||||
};
|
||||
}
|
||||
|
||||
private static IKeyValue Map(KeyValueDto dto)
|
||||
{
|
||||
if (dto == null) return null;
|
||||
|
||||
return new KeyValue
|
||||
{
|
||||
Identifier = dto.Key,
|
||||
Value = dto.Value,
|
||||
UpdateDate = dto.UpdateDate,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -132,7 +132,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// </summary>
|
||||
/// <remarks>This method is backed by an <see cref="IAppPolicyCache"/> cache</remarks>
|
||||
/// <param name="entity"></param>
|
||||
public void Save(TEntity entity)
|
||||
public virtual void Save(TEntity entity)
|
||||
{
|
||||
if (entity.HasIdentity == false)
|
||||
CachePolicy.Create(entity, PersistNewItem);
|
||||
|
||||
@@ -319,7 +319,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
private void PersistAllowedSections(IUserGroup entity)
|
||||
{
|
||||
var userGroup = (UserGroup) entity;
|
||||
var userGroup = entity;
|
||||
|
||||
// First delete all
|
||||
Database.Delete<UserGroup2AppDto>("WHERE UserGroupId = @UserGroupId", new { UserGroupId = userGroup.Id });
|
||||
|
||||
@@ -636,6 +636,11 @@ ORDER BY colName";
|
||||
}
|
||||
|
||||
public bool Exists(string username)
|
||||
{
|
||||
return ExistsByUserName(username);
|
||||
}
|
||||
|
||||
public bool ExistsByUserName(string username)
|
||||
{
|
||||
var sql = SqlContext.Sql()
|
||||
.SelectCount()
|
||||
@@ -645,6 +650,16 @@ ORDER BY colName";
|
||||
return Database.ExecuteScalar<int>(sql) > 0;
|
||||
}
|
||||
|
||||
public bool ExistsByLogin(string login)
|
||||
{
|
||||
var sql = SqlContext.Sql()
|
||||
.SelectCount()
|
||||
.From<UserDto>()
|
||||
.Where<UserDto>(x => x.Login == login);
|
||||
|
||||
return Database.ExecuteScalar<int>(sql) > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IUser"/> objects associated with a given group
|
||||
/// </summary>
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Umbraco.Core.Persistence
|
||||
private readonly RetryPolicy _connectionRetryPolicy;
|
||||
private readonly RetryPolicy _commandRetryPolicy;
|
||||
private readonly Guid _instanceGuid = Guid.NewGuid();
|
||||
private List<CommandInfo> _commands;
|
||||
|
||||
#region Ctor
|
||||
|
||||
@@ -162,6 +163,14 @@ namespace Umbraco.Core.Persistence
|
||||
/// </summary>
|
||||
public int SqlCount { get; private set; }
|
||||
|
||||
internal bool LogCommands
|
||||
{
|
||||
get => _commands != null;
|
||||
set => _commands = value ? new List<CommandInfo>() : null;
|
||||
}
|
||||
|
||||
internal IEnumerable<CommandInfo> Commands => _commands;
|
||||
|
||||
public int BulkInsertRecords<T>(IEnumerable<T> records)
|
||||
{
|
||||
return _bulkSqlInsertProvider.BulkInsertRecords(this, records);
|
||||
@@ -267,9 +276,43 @@ namespace Umbraco.Core.Persistence
|
||||
if (_enableCount)
|
||||
SqlCount++;
|
||||
|
||||
_commands?.Add(new CommandInfo(cmd));
|
||||
|
||||
base.OnExecutedCommand(cmd);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// used for tracking commands
|
||||
public class CommandInfo
|
||||
{
|
||||
public CommandInfo(IDbCommand cmd)
|
||||
{
|
||||
Text = cmd.CommandText;
|
||||
var parameters = new List<ParameterInfo>();
|
||||
foreach (IDbDataParameter parameter in cmd.Parameters) parameters.Add(new ParameterInfo(parameter));
|
||||
Parameters = parameters.ToArray();
|
||||
}
|
||||
|
||||
public string Text { get; }
|
||||
public ParameterInfo[] Parameters { get; }
|
||||
}
|
||||
|
||||
// used for tracking commands
|
||||
public class ParameterInfo
|
||||
{
|
||||
public ParameterInfo(IDbDataParameter parameter)
|
||||
{
|
||||
Name = parameter.ParameterName;
|
||||
Value = parameter.Value;
|
||||
DbType = parameter.DbType;
|
||||
Size = parameter.Size;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public object Value { get; }
|
||||
public DbType DbType { get; }
|
||||
public int Size { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
|
||||
namespace Umbraco.Core.Persistence
|
||||
{
|
||||
@@ -10,5 +11,20 @@ namespace Umbraco.Core.Persistence
|
||||
if (asDatabase == null) throw new Exception("oops: database.");
|
||||
return asDatabase;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a key/value directly from the database, no scope, nothing.
|
||||
/// </summary>
|
||||
/// <remarks>Used by <see cref="Runtime.CoreRuntime"/> to determine the runtime state.</remarks>
|
||||
public static string GetFromKeyValueTable(this IUmbracoDatabase database, string key)
|
||||
{
|
||||
if (database is null) return null;
|
||||
|
||||
var sql = database.SqlContext.Sql()
|
||||
.Select<KeyValueDto>()
|
||||
.From<KeyValueDto>()
|
||||
.Where<KeyValueDto>(x => x.Key == key);
|
||||
return database.FirstOrDefault<KeyValueDto>(sql)?.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
private readonly IDbProviderFactoryCreator _dbProviderFactoryCreator;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly IConnectionStrings _connectionStrings;
|
||||
private readonly Lazy<IMapperCollection> _mappers;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
@@ -37,7 +36,6 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
private DatabaseFactory _npocoDatabaseFactory;
|
||||
private IPocoDataFactory _pocoDataFactory;
|
||||
private string _connectionString;
|
||||
private string _providerName;
|
||||
private DatabaseType _databaseType;
|
||||
private ISqlSyntaxProvider _sqlSyntax;
|
||||
@@ -73,7 +71,7 @@ namespace Umbraco.Core.Persistence
|
||||
/// </summary>
|
||||
/// <remarks>Used by core runtime.</remarks>
|
||||
public UmbracoDatabaseFactory(ILogger logger, IGlobalSettings globalSettings, IConnectionStrings connectionStrings, Lazy<IMapperCollection> mappers,IDbProviderFactoryCreator dbProviderFactoryCreator)
|
||||
: this(Constants.System.UmbracoConnectionName, globalSettings, connectionStrings, logger, mappers, dbProviderFactoryCreator)
|
||||
: this(logger, globalSettings, connectionStrings, Constants.System.UmbracoConnectionName, mappers, dbProviderFactoryCreator)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -82,13 +80,12 @@ namespace Umbraco.Core.Persistence
|
||||
/// Initializes a new instance of the <see cref="UmbracoDatabaseFactory"/>.
|
||||
/// </summary>
|
||||
/// <remarks>Used by the other ctor and in tests.</remarks>
|
||||
public UmbracoDatabaseFactory(string connectionStringName, IGlobalSettings globalSettings, IConnectionStrings connectionStrings, ILogger logger, Lazy<IMapperCollection> mappers, IDbProviderFactoryCreator dbProviderFactoryCreator)
|
||||
public UmbracoDatabaseFactory(ILogger logger, IGlobalSettings globalSettings, IConnectionStrings connectionStrings, string connectionStringName, Lazy<IMapperCollection> mappers, IDbProviderFactoryCreator dbProviderFactoryCreator)
|
||||
{
|
||||
if (connectionStringName == null) throw new ArgumentNullException(nameof(connectionStringName));
|
||||
if (string.IsNullOrWhiteSpace(connectionStringName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(connectionStringName));
|
||||
|
||||
_globalSettings = globalSettings;
|
||||
_connectionStrings = connectionStrings;
|
||||
_mappers = mappers ?? throw new ArgumentNullException(nameof(mappers));
|
||||
_dbProviderFactoryCreator = dbProviderFactoryCreator ?? throw new ArgumentNullException(nameof(dbProviderFactoryCreator));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
@@ -118,7 +115,7 @@ namespace Umbraco.Core.Persistence
|
||||
/// Initializes a new instance of the <see cref="UmbracoDatabaseFactory"/>.
|
||||
/// </summary>
|
||||
/// <remarks>Used in tests.</remarks>
|
||||
public UmbracoDatabaseFactory(string connectionString, string providerName, ILogger logger, Lazy<IMapperCollection> mappers, IDbProviderFactoryCreator dbProviderFactoryCreator)
|
||||
public UmbracoDatabaseFactory(ILogger logger, string connectionString, string providerName, Lazy<IMapperCollection> mappers, IDbProviderFactoryCreator dbProviderFactoryCreator)
|
||||
{
|
||||
_mappers = mappers ?? throw new ArgumentNullException(nameof(mappers));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
@@ -142,7 +139,7 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return !_connectionString.IsNullOrWhiteSpace() && !_providerName.IsNullOrWhiteSpace();
|
||||
return !ConnectionString.IsNullOrWhiteSpace() && !_providerName.IsNullOrWhiteSpace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,13 +148,13 @@ namespace Umbraco.Core.Persistence
|
||||
public bool Initialized => Volatile.Read(ref _initialized);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ConnectionString => _connectionString;
|
||||
public string ConnectionString { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanConnect =>
|
||||
// actually tries to connect to the database (regardless of configured/initialized)
|
||||
!_connectionString.IsNullOrWhiteSpace() && !_providerName.IsNullOrWhiteSpace() &&
|
||||
DbConnectionExtensions.IsConnectionAvailable(_connectionString, DbProviderFactory);
|
||||
!ConnectionString.IsNullOrWhiteSpace() && !_providerName.IsNullOrWhiteSpace() &&
|
||||
DbConnectionExtensions.IsConnectionAvailable(ConnectionString, DbProviderFactory);
|
||||
|
||||
private void UpdateSqlServerDatabaseType()
|
||||
{
|
||||
@@ -169,7 +166,7 @@ namespace Umbraco.Core.Persistence
|
||||
if (setting.IsNullOrWhiteSpace() || !setting.StartsWith("SqlServer.")
|
||||
|| !Enum<SqlServerSyntaxProvider.VersionName>.TryParse(setting.Substring("SqlServer.".Length), out var versionName, true))
|
||||
{
|
||||
versionName = ((SqlServerSyntaxProvider) _sqlSyntax).GetSetVersion(_connectionString, _providerName, _logger).ProductVersionName;
|
||||
versionName = ((SqlServerSyntaxProvider) _sqlSyntax).GetSetVersion(ConnectionString, _providerName, _logger).ProductVersionName;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -233,7 +230,7 @@ namespace Umbraco.Core.Persistence
|
||||
if (Volatile.Read(ref _initialized))
|
||||
throw new InvalidOperationException("Already initialized.");
|
||||
|
||||
_connectionString = connectionString;
|
||||
ConnectionString = connectionString;
|
||||
_providerName = providerName;
|
||||
}
|
||||
|
||||
@@ -249,18 +246,19 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
_logger.Debug<UmbracoDatabaseFactory>("Initializing.");
|
||||
|
||||
if (_connectionString.IsNullOrWhiteSpace()) throw new InvalidOperationException("The factory has not been configured with a proper connection string.");
|
||||
if (ConnectionString.IsNullOrWhiteSpace()) throw new InvalidOperationException("The factory has not been configured with a proper connection string.");
|
||||
if (_providerName.IsNullOrWhiteSpace()) throw new InvalidOperationException("The factory has not been configured with a proper provider name.");
|
||||
|
||||
if (DbProviderFactory == null)
|
||||
throw new Exception($"Can't find a provider factory for provider name \"{_providerName}\".");
|
||||
|
||||
// cannot initialize without being able to talk to the database
|
||||
if (!DbConnectionExtensions.IsConnectionAvailable(_connectionString, DbProviderFactory))
|
||||
// TODO: Why not?
|
||||
if (!DbConnectionExtensions.IsConnectionAvailable(ConnectionString, DbProviderFactory))
|
||||
throw new Exception("Cannot connect to the database.");
|
||||
|
||||
_connectionRetryPolicy = RetryPolicyFactory.GetDefaultSqlConnectionRetryPolicyByConnectionString(_connectionString);
|
||||
_commandRetryPolicy = RetryPolicyFactory.GetDefaultSqlCommandRetryPolicyByConnectionString(_connectionString);
|
||||
_connectionRetryPolicy = RetryPolicyFactory.GetDefaultSqlConnectionRetryPolicyByConnectionString(ConnectionString);
|
||||
_commandRetryPolicy = RetryPolicyFactory.GetDefaultSqlCommandRetryPolicyByConnectionString(ConnectionString);
|
||||
|
||||
|
||||
_databaseType = DatabaseType.Resolve(DbProviderFactory.GetType().Name, _providerName);
|
||||
@@ -313,7 +311,7 @@ namespace Umbraco.Core.Persistence
|
||||
// method used by NPoco's UmbracoDatabaseFactory to actually create the database instance
|
||||
private UmbracoDatabase CreateDatabaseInstance()
|
||||
{
|
||||
return new UmbracoDatabase(_connectionString, SqlContext, DbProviderFactory, _logger, _bulkSqlInsertProvider, _connectionRetryPolicy, _commandRetryPolicy);
|
||||
return new UmbracoDatabase(ConnectionString, SqlContext, DbProviderFactory, _logger, _bulkSqlInsertProvider, _connectionRetryPolicy, _commandRetryPolicy);
|
||||
}
|
||||
|
||||
protected override void DisposeResources()
|
||||
|
||||
@@ -5,6 +5,7 @@ using Newtonsoft.Json;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Media;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
@@ -6,6 +6,7 @@ using Umbraco.Core;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Media;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Strings;
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Media;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Media;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Serialization;
|
||||
using Umbraco.Core.Strings;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Composing.CompositionExtensions;
|
||||
@@ -7,9 +6,8 @@ using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Grid;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Dashboards;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Dictionary;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Manifest;
|
||||
using Umbraco.Core.Migrations;
|
||||
@@ -25,14 +23,14 @@ using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Services.Implement;
|
||||
using Umbraco.Core.Strings;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Web.Models.PublishedContent;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.Migrations.PostMigrations;
|
||||
using Umbraco.Web.Install;
|
||||
using Umbraco.Web.Trees;
|
||||
using Umbraco.Web.Migrations.PostMigrations;
|
||||
using Umbraco.Web.Models.PublishedContent;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
using Umbraco.Web.Services;
|
||||
using Umbraco.Web.Trees;
|
||||
using IntegerValidator = Umbraco.Core.PropertyEditors.Validators.IntegerValidator;
|
||||
|
||||
namespace Umbraco.Core.Runtime
|
||||
|
||||
@@ -33,14 +33,12 @@ namespace Umbraco.Core.Runtime
|
||||
// unique id for our appdomain, this is more unique than the appdomain id which is just an INT counter to its safer
|
||||
_lockId = Guid.NewGuid().ToString();
|
||||
_logger = logger;
|
||||
_dbFactory = new UmbracoDatabaseFactory(
|
||||
Constants.System.UmbracoConnectionName,
|
||||
_dbFactory = new UmbracoDatabaseFactory(_logger,
|
||||
globalSettings,
|
||||
connectionStrings,
|
||||
_logger,
|
||||
Constants.System.UmbracoConnectionName,
|
||||
new Lazy<IMapperCollection>(() => new MapperCollection(Enumerable.Empty<BaseMapper>())),
|
||||
dbProviderFactoryCreator
|
||||
);
|
||||
dbProviderFactoryCreator);
|
||||
}
|
||||
|
||||
public async Task<bool> AcquireLockAsync(int millisecondsTimeout)
|
||||
@@ -326,7 +324,7 @@ namespace Umbraco.Core.Runtime
|
||||
{
|
||||
Key = MainDomKey,
|
||||
Value = id,
|
||||
Updated = DateTime.Now
|
||||
UpdateDate = DateTime.Now
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Migrations.Upgrade;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Services.Implement;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.Sync;
|
||||
|
||||
namespace Umbraco.Core
|
||||
@@ -225,7 +225,7 @@ namespace Umbraco.Core
|
||||
// no scope, no service - just directly accessing the database
|
||||
using (var database = databaseFactory.CreateDatabase())
|
||||
{
|
||||
CurrentMigrationState = KeyValueService.GetValue(database, stateValueKey);
|
||||
CurrentMigrationState = database.GetFromKeyValueTable(stateValueKey);
|
||||
FinalMigrationState = upgrader.Plan.FinalState;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,168 +1,55 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Migrations;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
|
||||
namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
internal class KeyValueService : IKeyValueService
|
||||
{
|
||||
private readonly object _initialock = new object();
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUmbracoVersion _umbracoVersion;
|
||||
private bool _initialized;
|
||||
private readonly IKeyValueRepository _repository;
|
||||
|
||||
public KeyValueService(IScopeProvider scopeProvider, ILogger logger, IUmbracoVersion umbracoVersion)
|
||||
public KeyValueService(IScopeProvider scopeProvider, IKeyValueRepository repository)
|
||||
{
|
||||
_scopeProvider = scopeProvider;
|
||||
_logger = logger;
|
||||
_umbracoVersion = umbracoVersion;
|
||||
}
|
||||
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
lock (_initialock)
|
||||
{
|
||||
if (_initialized) return;
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
// the key/value service is entirely self-managed, because it is used by the
|
||||
// upgrader and anything we might change need to happen before everything else
|
||||
|
||||
// if already running 8, either following an upgrade or an install,
|
||||
// then everything should be ok (the table should exist, etc)
|
||||
|
||||
if (_umbracoVersion.LocalVersion != null && _umbracoVersion.LocalVersion.Major >= 8)
|
||||
{
|
||||
_initialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// else we are upgrading from 7, we can assume that the locks table
|
||||
// exists, but we need to create everything for key/value
|
||||
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
var context = new MigrationContext(scope.Database, _logger);
|
||||
var initMigration = new InitializeMigration(context);
|
||||
initMigration.Migrate();
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
// but don't assume we are initializing
|
||||
// we are upgrading from v7 and if anything goes wrong,
|
||||
// the table and everything will be rolled back
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A custom migration that executes standalone during the Initialize phase of this service.
|
||||
/// </summary>
|
||||
internal class InitializeMigration : MigrationBase
|
||||
{
|
||||
public InitializeMigration(IMigrationContext context)
|
||||
: base(context)
|
||||
{ }
|
||||
|
||||
public override void Migrate()
|
||||
{
|
||||
// as long as we are still running 7 this migration will be invoked,
|
||||
// but due to multiple restarts during upgrades, maybe the table
|
||||
// exists already
|
||||
if (TableExists(Constants.DatabaseSchema.Tables.KeyValue))
|
||||
return;
|
||||
|
||||
Logger.Info<KeyValueService>("Creating KeyValue structure.");
|
||||
|
||||
// the locks table was initially created with an identity (auto-increment) primary key,
|
||||
// but we don't want this, especially as we are about to insert a new row into the table,
|
||||
// so here we drop that identity
|
||||
DropLockTableIdentity();
|
||||
|
||||
// insert the lock object for key/value
|
||||
Insert.IntoTable(Constants.DatabaseSchema.Tables.Lock).Row(new {id = Constants.Locks.KeyValues, name = "KeyValues", value = 1}).Do();
|
||||
|
||||
// create the key-value table
|
||||
Create.Table<KeyValueDto>().Do();
|
||||
}
|
||||
|
||||
private void DropLockTableIdentity()
|
||||
{
|
||||
// one cannot simply drop an identity, that requires a bit of work
|
||||
|
||||
// create a temp. id column and copy values
|
||||
Alter.Table(Constants.DatabaseSchema.Tables.Lock).AddColumn("nid").AsInt32().Nullable().Do();
|
||||
Execute.Sql("update umbracoLock set nid = id").Do();
|
||||
|
||||
// drop the id column entirely (cannot just drop identity)
|
||||
Delete.PrimaryKey("PK_umbracoLock").FromTable(Constants.DatabaseSchema.Tables.Lock).Do();
|
||||
Delete.Column("id").FromTable(Constants.DatabaseSchema.Tables.Lock).Do();
|
||||
|
||||
// recreate the id column without identity and copy values
|
||||
Alter.Table(Constants.DatabaseSchema.Tables.Lock).AddColumn("id").AsInt32().Nullable().Do();
|
||||
Execute.Sql("update umbracoLock set id = nid").Do();
|
||||
|
||||
// drop the temp. id column
|
||||
Delete.Column("nid").FromTable(Constants.DatabaseSchema.Tables.Lock).Do();
|
||||
|
||||
// complete the primary key
|
||||
Alter.Table(Constants.DatabaseSchema.Tables.Lock).AlterColumn("id").AsInt32().NotNullable().PrimaryKey("PK_umbracoLock").Do();
|
||||
}
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetValue(string key)
|
||||
{
|
||||
EnsureInitialized();
|
||||
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
var sql = scope.SqlContext.Sql().Select<KeyValueDto>().From<KeyValueDto>().Where<KeyValueDto>(x => x.Key == key);
|
||||
var dto = scope.Database.Fetch<KeyValueDto>(sql).FirstOrDefault();
|
||||
scope.Complete();
|
||||
return dto?.Value;
|
||||
return _repository.Get(key)?.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetValue(string key, string value)
|
||||
{
|
||||
EnsureInitialized();
|
||||
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.KeyValues);
|
||||
|
||||
var sql = scope.SqlContext.Sql().Select<KeyValueDto>().From<KeyValueDto>().Where<KeyValueDto>(x => x.Key == key);
|
||||
var dto = scope.Database.Fetch<KeyValueDto>(sql).FirstOrDefault();
|
||||
|
||||
if (dto == null)
|
||||
var keyValue = _repository.Get(key);
|
||||
if (keyValue == null)
|
||||
{
|
||||
dto = new KeyValueDto
|
||||
keyValue = new KeyValue
|
||||
{
|
||||
Key = key,
|
||||
Identifier = key,
|
||||
Value = value,
|
||||
Updated = DateTime.Now
|
||||
UpdateDate = DateTime.Now,
|
||||
};
|
||||
|
||||
scope.Database.Insert(dto);
|
||||
}
|
||||
else
|
||||
{
|
||||
dto.Value = value;
|
||||
dto.Updated = DateTime.Now;
|
||||
scope.Database.Update(dto);
|
||||
keyValue.Value = value;
|
||||
keyValue.UpdateDate = DateTime.Now;
|
||||
}
|
||||
|
||||
_repository.Save(keyValue);
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -175,43 +62,26 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TrySetValue(string key, string originValue, string newValue)
|
||||
public bool TrySetValue(string key, string originalValue, string newValue)
|
||||
{
|
||||
EnsureInitialized();
|
||||
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.KeyValues);
|
||||
|
||||
var sql = scope.SqlContext.Sql().Select<KeyValueDto>().From<KeyValueDto>().Where<KeyValueDto>(x => x.Key == key);
|
||||
var dto = scope.Database.Fetch<KeyValueDto>(sql).FirstOrDefault();
|
||||
|
||||
if (dto == null || dto.Value != originValue)
|
||||
var keyValue = _repository.Get(key);
|
||||
if (keyValue == null || keyValue.Value != originalValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
dto.Value = newValue;
|
||||
dto.Updated = DateTime.Now;
|
||||
scope.Database.Update(dto);
|
||||
keyValue.Value = newValue;
|
||||
keyValue.UpdateDate = DateTime.Now;
|
||||
_repository.Save(keyValue);
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value directly from the database, no scope, nothing.
|
||||
/// </summary>
|
||||
/// <remarks>Used by <see cref="Runtime.CoreRuntime"/> to determine the runtime state.</remarks>
|
||||
internal static string GetValue(IUmbracoDatabase database, string key)
|
||||
{
|
||||
if (database is null) return null;
|
||||
|
||||
var sql = database.SqlContext.Sql()
|
||||
.Select<KeyValueDto>()
|
||||
.From<KeyValueDto>()
|
||||
.Where<KeyValueDto>(x => x.Key == key);
|
||||
return database.FirstOrDefault<KeyValueDto>(sql)?.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -756,7 +756,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
throw new ArgumentOutOfRangeException(nameof(matchType)); // causes rollback // causes rollback
|
||||
}
|
||||
|
||||
// TODO: Since this is by property value, we need a GetByPropertyQuery on the repo!
|
||||
// TODO: Since this is by property value, we need a GetByPropertyQuery on the repo!
|
||||
return _memberRepository.Get(query);
|
||||
}
|
||||
@@ -1196,7 +1195,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.MemberTree);
|
||||
|
||||
// TODO: What about content that has the contenttype as part of its composition?
|
||||
// TODO: What about content that has the contenttype as part of its composition?
|
||||
var query = Query<IMember>().Where(x => x.ContentTypeId == memberTypeId);
|
||||
|
||||
|
||||
@@ -124,7 +124,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
else
|
||||
{
|
||||
//If they are both the same already then there's nothing to update, exit
|
||||
//If they are both the same already then there's nothing to update, exit
|
||||
return OperationResult.Attempt.Succeed(evtMsgs, entry);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
_serverRegistrationRepository.Save(server);
|
||||
_serverRegistrationRepository.DeactiveStaleServers(staleTimeout); // triggers a cache reload
|
||||
|
||||
// reload - cheap, cached
|
||||
// reload - cheap, cached
|
||||
|
||||
// default role is single server, but if registrations contain more
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
return _userRepository.Exists(username);
|
||||
return _userRepository.ExistsByUserName(username);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,9 +109,9 @@ namespace Umbraco.Core.Services.Implement
|
||||
User user;
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var loginExists = scope.Database.ExecuteScalar<int>("SELECT COUNT(id) FROM umbracoUser WHERE userLogin = @Login", new { Login = username }) != 0;
|
||||
var loginExists = _userRepository.ExistsByLogin(username);
|
||||
if (loginExists)
|
||||
throw new ArgumentException("Login already exists"); // causes rollback // causes rollback
|
||||
throw new ArgumentException("Login already exists"); // causes rollback
|
||||
|
||||
user = new User(_globalSettings)
|
||||
{
|
||||
@@ -340,7 +340,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
_userRepository.Save(user);
|
||||
|
||||
//Now we have to check for backwards compat hacks
|
||||
//Now we have to check for backwards compat hacks
|
||||
var explicitUser = user as User;
|
||||
if (explicitUser != null && explicitUser.GroupsToSave.Count > 0)
|
||||
@@ -358,7 +357,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
scope.Events.Dispatch(SavedUser, this, saveEventArgs);
|
||||
}
|
||||
|
||||
//commit the whole lot in one go
|
||||
//commit the whole lot in one go
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
106
src/Umbraco.Tests.Common/Builders/UserBuilder.cs
Normal file
106
src/Umbraco.Tests.Common/Builders/UserBuilder.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Tests.Common.Builders.Interfaces;
|
||||
|
||||
namespace Umbraco.Tests.Common.Builders
|
||||
{
|
||||
public class UserBuilder
|
||||
: BuilderBase<User>,
|
||||
IWithIdBuilder
|
||||
{
|
||||
private int? _id;
|
||||
private string _language;
|
||||
private bool? _approved;
|
||||
private string _name;
|
||||
private string _rawPassword;
|
||||
private bool? _isLockedOut;
|
||||
private string _email;
|
||||
private string _username;
|
||||
private string _defaultLang;
|
||||
private string _suffix = string.Empty;
|
||||
|
||||
public UserBuilder WithDefaultUILanguage(string defaultLang)
|
||||
{
|
||||
_defaultLang = defaultLang;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder WithLanguage(string language)
|
||||
{
|
||||
_language = language;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder WithApproved(bool approved)
|
||||
{
|
||||
_approved = approved;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder WithRawPassword(string rawPassword)
|
||||
{
|
||||
_rawPassword = rawPassword;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder WithEmail(string email)
|
||||
{
|
||||
_email = email;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder WithUsername(string username)
|
||||
{
|
||||
_username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder WithLockedOut(bool isLockedOut)
|
||||
{
|
||||
_isLockedOut = isLockedOut;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder WithName(string name)
|
||||
{
|
||||
_name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will suffix the name, email and username for testing
|
||||
/// </summary>
|
||||
/// <param name="suffix"></param>
|
||||
/// <returns></returns>
|
||||
public UserBuilder WithSuffix(string suffix)
|
||||
{
|
||||
_suffix = suffix;
|
||||
return this;
|
||||
}
|
||||
|
||||
public override User Build()
|
||||
{
|
||||
var globalSettings = Mock.Of<IGlobalSettings>(x => x.DefaultUILanguage == (_defaultLang ?? "en-US"));
|
||||
return new User(globalSettings,
|
||||
_name ?? "TestUser" + _suffix,
|
||||
_email ?? "test" + _suffix + "@test.com",
|
||||
_username ?? "TestUser" + _suffix,
|
||||
_rawPassword ?? "abcdefghijklmnopqrstuvwxyz")
|
||||
{
|
||||
Language = _language ?? _defaultLang ?? "en-US",
|
||||
IsLockedOut = _isLockedOut ?? false,
|
||||
IsApproved = _approved ?? true
|
||||
};
|
||||
}
|
||||
|
||||
int? IWithIdBuilder.Id
|
||||
{
|
||||
get => _id;
|
||||
set => _id = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
src/Umbraco.Tests.Common/Builders/UserGroupBuilder.cs
Normal file
65
src/Umbraco.Tests.Common/Builders/UserGroupBuilder.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Tests.Common.Builders.Interfaces;
|
||||
|
||||
namespace Umbraco.Tests.Common.Builders
|
||||
{
|
||||
public class UserGroupBuilder
|
||||
: BuilderBase<IUserGroup>,
|
||||
IWithIdBuilder
|
||||
{
|
||||
private int? _startContentId;
|
||||
private int? _startMediaId;
|
||||
private string _alias;
|
||||
private string _icon;
|
||||
private string _name;
|
||||
private IEnumerable<string> _permissions = Enumerable.Empty<string>();
|
||||
private IEnumerable<string> _sectionCollection = Enumerable.Empty<string>();
|
||||
private string _suffix;
|
||||
private int? _id;
|
||||
|
||||
/// <summary>
|
||||
/// Will suffix the name and alias for testing
|
||||
/// </summary>
|
||||
/// <param name="suffix"></param>
|
||||
/// <returns></returns>
|
||||
public UserGroupBuilder WithSuffix(string suffix)
|
||||
{
|
||||
_suffix = suffix;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IReadOnlyUserGroup BuildReadOnly(IUserGroup userGroup)
|
||||
{
|
||||
return Mock.Of<IReadOnlyUserGroup>(x =>
|
||||
x.Permissions == userGroup.Permissions &&
|
||||
x.Alias == userGroup.Alias &&
|
||||
x.Icon == userGroup.Icon &&
|
||||
x.Name == userGroup.Name &&
|
||||
x.StartContentId == userGroup.StartContentId &&
|
||||
x.StartMediaId == userGroup.StartMediaId &&
|
||||
x.AllowedSections == userGroup.AllowedSections &&
|
||||
x.Id == userGroup.Id);
|
||||
}
|
||||
|
||||
public override IUserGroup Build()
|
||||
{
|
||||
return Mock.Of<IUserGroup>(x =>
|
||||
x.StartContentId == _startContentId &&
|
||||
x.StartMediaId == _startMediaId &&
|
||||
x.Name == (_name ?? ("TestUserGroup" + _suffix)) &&
|
||||
x.Alias == (_alias ?? ("testUserGroup" + _suffix)) &&
|
||||
x.Icon == _icon &&
|
||||
x.Permissions == _permissions &&
|
||||
x.AllowedSections == _sectionCollection);
|
||||
}
|
||||
|
||||
int? IWithIdBuilder.Id
|
||||
{
|
||||
get => _id;
|
||||
set => _id = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,10 +31,14 @@ namespace Umbraco.Tests.Testing
|
||||
var test = TestContext.CurrentContext.Test;
|
||||
var typeName = test.ClassName;
|
||||
var methodName = test.MethodName;
|
||||
// this will only get types from whatever is already loaded in the app domain
|
||||
var type = Type.GetType(typeName, false);
|
||||
if (type == null)
|
||||
{
|
||||
type = ScanAssemblies
|
||||
// automatically add the executing and calling assemblies to the list to scan for this type
|
||||
var scanAssemblies = ScanAssemblies.Union(new[] {Assembly.GetExecutingAssembly(), Assembly.GetCallingAssembly()}).ToList();
|
||||
|
||||
type = scanAssemblies
|
||||
.Select(assembly => assembly.GetType(typeName, false))
|
||||
.FirstOrDefault(x => x != null);
|
||||
if (type == null)
|
||||
61
src/Umbraco.Tests.Common/Testing/UmbracoTestOptions.cs
Normal file
61
src/Umbraco.Tests.Common/Testing/UmbracoTestOptions.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
namespace Umbraco.Tests.Testing
|
||||
{
|
||||
public static class UmbracoTestOptions
|
||||
{
|
||||
public enum Logger
|
||||
{
|
||||
/// <summary>
|
||||
/// pure mocks
|
||||
/// </summary>
|
||||
Mock,
|
||||
/// <summary>
|
||||
/// Serilog for tests
|
||||
/// </summary>
|
||||
Serilog,
|
||||
/// <summary>
|
||||
/// console logger
|
||||
/// </summary>
|
||||
Console
|
||||
}
|
||||
|
||||
public enum Database
|
||||
{
|
||||
/// <summary>
|
||||
/// no database
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// new empty database file for the entire fixture
|
||||
/// </summary>
|
||||
NewEmptyPerFixture,
|
||||
/// <summary>
|
||||
/// new empty database file per test
|
||||
/// </summary>
|
||||
NewEmptyPerTest,
|
||||
/// <summary>
|
||||
/// new database file with schema for the entire fixture
|
||||
/// </summary>
|
||||
NewSchemaPerFixture,
|
||||
/// <summary>
|
||||
/// new database file with schema per test
|
||||
/// </summary>
|
||||
NewSchemaPerTest
|
||||
}
|
||||
|
||||
public enum TypeLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// the default, global type loader for tests
|
||||
/// </summary>
|
||||
Default,
|
||||
/// <summary>
|
||||
/// create one type loader for the feature
|
||||
/// </summary>
|
||||
PerFixture,
|
||||
/// <summary>
|
||||
/// create one type loader for each test
|
||||
/// </summary>
|
||||
PerTest
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Tests.Common;
|
||||
using Umbraco.Tests.Integration.Implementations;
|
||||
using Umbraco.Tests.Integration.Testing;
|
||||
|
||||
namespace Umbraco.Tests.Integration
|
||||
{
|
||||
@@ -82,7 +83,7 @@ namespace Umbraco.Tests.Integration
|
||||
// it means the container won't be disposed, and maybe other services? not sure.
|
||||
// In cases where we use it can we use IConfigureOptions? https://andrewlock.net/access-services-inside-options-and-startup-using-configureoptions/
|
||||
|
||||
var umbracoContainer = RuntimeTests.GetUmbracoContainer(out var serviceProviderFactory);
|
||||
var umbracoContainer = UmbracoIntegrationTest.GetUmbracoContainer(out var serviceProviderFactory);
|
||||
|
||||
IHostApplicationLifetime lifetime1 = null;
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Configuration.Models;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Tests.Integration.Testing;
|
||||
using Umbraco.Tests.Testing;
|
||||
|
||||
namespace Umbraco.Tests.Integration.Extensions
|
||||
{
|
||||
public static class ApplicationBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a LocalDb instance to use for the test
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="dbFilePath"></param>
|
||||
/// <param name="integrationTest"></param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseTestLocalDb(this IApplicationBuilder app,
|
||||
string dbFilePath,
|
||||
UmbracoIntegrationTest integrationTest)
|
||||
{
|
||||
// get the currently set db options
|
||||
var testOptions = TestOptionAttributeBase.GetTestOptions<UmbracoTestAttribute>();
|
||||
|
||||
if (testOptions.Database == UmbracoTestOptions.Database.None)
|
||||
return app;
|
||||
|
||||
// need to manually register this factory
|
||||
DbProviderFactories.RegisterFactory(Constants.DbProviderNames.SqlServer, SqlClientFactory.Instance);
|
||||
|
||||
if (!Directory.Exists(dbFilePath))
|
||||
Directory.CreateDirectory(dbFilePath);
|
||||
|
||||
var db = UmbracoIntegrationTest.GetOrCreate(dbFilePath,
|
||||
app.ApplicationServices.GetRequiredService<ILogger>(),
|
||||
app.ApplicationServices.GetRequiredService<IGlobalSettings>(),
|
||||
app.ApplicationServices.GetRequiredService<IUmbracoDatabaseFactory>());
|
||||
|
||||
switch (testOptions.Database)
|
||||
{
|
||||
case UmbracoTestOptions.Database.NewSchemaPerTest:
|
||||
|
||||
// Add teardown callback
|
||||
integrationTest.OnTestTearDown(() => db.Detach());
|
||||
|
||||
// New DB + Schema
|
||||
db.AttachSchema();
|
||||
|
||||
// We must re-configure our current factory since attaching a new LocalDb from the pool changes connection strings
|
||||
var dbFactory = app.ApplicationServices.GetRequiredService<IUmbracoDatabaseFactory>();
|
||||
if (!dbFactory.Configured)
|
||||
{
|
||||
dbFactory.Configure(db.ConnectionString, Umbraco.Core.Constants.DatabaseProviders.SqlServer);
|
||||
}
|
||||
|
||||
// In the case that we've initialized the schema, it means that we are installed so we'll want to ensure that
|
||||
// the runtime state is configured correctly so we'll force update the configuration flag and re-run the
|
||||
// runtime state checker.
|
||||
// TODO: This wouldn't be required if we don't store the Umbraco version in config
|
||||
|
||||
// right now we are an an 'Install' state
|
||||
var runtimeState = (RuntimeState)app.ApplicationServices.GetRequiredService<IRuntimeState>();
|
||||
Assert.AreEqual(RuntimeLevel.Install, runtimeState.Level);
|
||||
|
||||
// dynamically change the config status
|
||||
var umbVersion = app.ApplicationServices.GetRequiredService<IUmbracoVersion>();
|
||||
var config = app.ApplicationServices.GetRequiredService<IConfiguration>();
|
||||
config[GlobalSettings.Prefix + "ConfigurationStatus"] = umbVersion.SemanticVersion.ToString();
|
||||
|
||||
// re-run the runtime level check
|
||||
var profilingLogger = app.ApplicationServices.GetRequiredService<IProfilingLogger>();
|
||||
runtimeState.DetermineRuntimeLevel(dbFactory, profilingLogger);
|
||||
|
||||
Assert.AreEqual(RuntimeLevel.Run, runtimeState.Level);
|
||||
|
||||
break;
|
||||
case UmbracoTestOptions.Database.NewEmptyPerTest:
|
||||
|
||||
// Add teardown callback
|
||||
integrationTest.OnTestTearDown(() => db.Detach());
|
||||
|
||||
db.AttachEmpty();
|
||||
|
||||
break;
|
||||
case UmbracoTestOptions.Database.NewSchemaPerFixture:
|
||||
|
||||
// Add teardown callback
|
||||
integrationTest.OnFixtureTearDown(() => db.Detach());
|
||||
|
||||
break;
|
||||
case UmbracoTestOptions.Database.NewEmptyPerFixture:
|
||||
|
||||
// Add teardown callback
|
||||
integrationTest.OnFixtureTearDown(() => db.Detach());
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(testOptions), testOptions, null);
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Tests.Integration.Implementations;
|
||||
|
||||
namespace Umbraco.Tests.Integration.Extensions
|
||||
{
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// These services need to be manually added because they do not get added by the generic host
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="testHelper"></param>
|
||||
/// <param name="webHostEnvironment"></param>
|
||||
public static void AddRequiredNetCoreServices(this IServiceCollection services, TestHelper testHelper, IWebHostEnvironment webHostEnvironment)
|
||||
{
|
||||
services.AddSingleton<IHttpContextAccessor>(x => testHelper.GetHttpContextAccessor());
|
||||
// the generic host does add IHostEnvironment but not this one because we are not actually in a web context
|
||||
services.AddSingleton<IWebHostEnvironment>(x => webHostEnvironment);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/Umbraco.Tests.Integration/GlobalSetupTeardown.cs
Normal file
29
src/Umbraco.Tests.Integration/GlobalSetupTeardown.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Tests.Integration.Testing;
|
||||
|
||||
// this class has NO NAMESPACE
|
||||
// it applies to the whole assembly
|
||||
|
||||
[SetUpFixture]
|
||||
// ReSharper disable once CheckNamespace
|
||||
public class TestsSetup
|
||||
{
|
||||
private Stopwatch _stopwatch;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_stopwatch = Stopwatch.StartNew();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
LocalDbTestDatabase.KillLocalDb();
|
||||
Console.WriteLine("TOTAL TESTS DURATION: {0}", _stopwatch.Elapsed);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Tests.Integration.Testing;
|
||||
|
||||
namespace Umbraco.Tests.Integration.Implementations
|
||||
{
|
||||
public static class HostBuilderExtensions
|
||||
{
|
||||
|
||||
public static IHostBuilder UseLocalDb(this IHostBuilder hostBuilder, string dbFilePath)
|
||||
{
|
||||
// Need to register SqlClient manually
|
||||
// TODO: Move this to someplace central
|
||||
DbProviderFactories.RegisterFactory(Constants.DbProviderNames.SqlServer, SqlClientFactory.Instance);
|
||||
|
||||
hostBuilder.ConfigureAppConfiguration(x =>
|
||||
{
|
||||
if (!Directory.Exists(dbFilePath))
|
||||
Directory.CreateDirectory(dbFilePath);
|
||||
|
||||
var dbName = Guid.NewGuid().ToString("N");
|
||||
var instance = TestLocalDb.EnsureLocalDbInstanceAndDatabase(dbName, dbFilePath);
|
||||
|
||||
x.AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>($"ConnectionStrings:{Constants.System.UmbracoConnectionName}", instance.GetConnectionString(dbName))
|
||||
});
|
||||
});
|
||||
return hostBuilder;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@ using Umbraco.Core.Runtime;
|
||||
using Umbraco.Tests.Common;
|
||||
using Umbraco.Web.BackOffice;
|
||||
using Umbraco.Web.BackOffice.AspNetCore;
|
||||
using Umbraco.Web.Common.AspNetCore;
|
||||
using IHostingEnvironment = Umbraco.Core.Hosting.IHostingEnvironment;
|
||||
|
||||
namespace Umbraco.Tests.Integration.Implementations
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Web.BackOffice.AspNetCore;
|
||||
using Umbraco.Web.Common.AspNetCore;
|
||||
|
||||
namespace Umbraco.Tests.Integration.Implementations
|
||||
{
|
||||
|
||||
|
||||
public class TestHostingEnvironment : AspNetCoreHostingEnvironment, Umbraco.Core.Hosting.IHostingEnvironment
|
||||
{
|
||||
public TestHostingEnvironment(IHostingSettings hostingSettings, IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContextAccessor) : base(hostingSettings, webHostEnvironment, httpContextAccessor)
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.Integration.Testing;
|
||||
using Umbraco.Tests.Testing;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Tests.Persistence.Repositories
|
||||
namespace Umbraco.Tests.Integration.Persistence.Repositories
|
||||
{
|
||||
[TestFixture]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Console)]
|
||||
public class AuditRepositoryTest : TestWithDatabaseBase
|
||||
public class AuditRepositoryTest : UmbracoIntegrationTest
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void Can_Add_Audit_Entry()
|
||||
{
|
||||
var sp = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = sp.CreateScope())
|
||||
var sp = ScopeProvider;
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var repo = new AuditRepository((IScopeAccessor) sp, Logger);
|
||||
repo.Save(new AuditItem(-1, AuditType.System, -1, ObjectTypes.GetName(UmbracoObjectTypes.Document), "This is a System audit trail"));
|
||||
var repo = new AuditRepository((IScopeAccessor)sp, Logger);
|
||||
repo.Save(new AuditItem(-1, AuditType.System, -1, UmbracoObjectTypes.Document.GetName(), "This is a System audit trail"));
|
||||
|
||||
var dtos = scope.Database.Fetch<LogDto>("WHERE id > -1");
|
||||
|
||||
@@ -35,15 +35,15 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
[Test]
|
||||
public void Get_Paged_Items()
|
||||
{
|
||||
var sp = TestObjects.GetScopeProvider(Logger);
|
||||
var sp = ScopeProvider;
|
||||
using (var scope = sp.CreateScope())
|
||||
{
|
||||
var repo = new AuditRepository((IScopeAccessor) sp, Logger);
|
||||
var repo = new AuditRepository((IScopeAccessor)sp, Logger);
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
repo.Save(new AuditItem(i, AuditType.New, -1, ObjectTypes.GetName(UmbracoObjectTypes.Document), $"Content {i} created"));
|
||||
repo.Save(new AuditItem(i, AuditType.Publish, -1, ObjectTypes.GetName(UmbracoObjectTypes.Document), $"Content {i} published"));
|
||||
repo.Save(new AuditItem(i, AuditType.New, -1, UmbracoObjectTypes.Document.GetName(), $"Content {i} created"));
|
||||
repo.Save(new AuditItem(i, AuditType.Publish, -1, UmbracoObjectTypes.Document.GetName(), $"Content {i} published"));
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
@@ -51,7 +51,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
|
||||
using (var scope = sp.CreateScope())
|
||||
{
|
||||
var repo = new AuditRepository((IScopeAccessor) sp, Logger);
|
||||
var repo = new AuditRepository((IScopeAccessor)sp, Logger);
|
||||
|
||||
var page = repo.GetPagedResultsByQuery(sp.SqlContext.Query<IAuditItem>(), 0, 10, out var total, Direction.Descending, null, null);
|
||||
|
||||
@@ -63,15 +63,15 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
[Test]
|
||||
public void Get_Paged_Items_By_User_Id_With_Query_And_Filter()
|
||||
{
|
||||
var sp = TestObjects.GetScopeProvider(Logger);
|
||||
var sp = ScopeProvider;
|
||||
using (var scope = sp.CreateScope())
|
||||
{
|
||||
var repo = new AuditRepository((IScopeAccessor)sp, Logger);
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
repo.Save(new AuditItem(i, AuditType.New, -1, ObjectTypes.GetName(UmbracoObjectTypes.Document), $"Content {i} created"));
|
||||
repo.Save(new AuditItem(i, AuditType.Publish, -1, ObjectTypes.GetName(UmbracoObjectTypes.Document), $"Content {i} published"));
|
||||
repo.Save(new AuditItem(i, AuditType.New, -1, UmbracoObjectTypes.Document.GetName(), $"Content {i} created"));
|
||||
repo.Save(new AuditItem(i, AuditType.Publish, -1, UmbracoObjectTypes.Document.GetName(), $"Content {i} published"));
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
@@ -106,15 +106,15 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
[Test]
|
||||
public void Get_Paged_Items_With_AuditType_Filter()
|
||||
{
|
||||
var sp = TestObjects.GetScopeProvider(Logger);
|
||||
var sp = ScopeProvider;
|
||||
using (var scope = sp.CreateScope())
|
||||
{
|
||||
var repo = new AuditRepository((IScopeAccessor) sp, Logger);
|
||||
var repo = new AuditRepository((IScopeAccessor)sp, Logger);
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
repo.Save(new AuditItem(i, AuditType.New, -1, ObjectTypes.GetName(UmbracoObjectTypes.Document), $"Content {i} created"));
|
||||
repo.Save(new AuditItem(i, AuditType.Publish, -1, ObjectTypes.GetName(UmbracoObjectTypes.Document), $"Content {i} published"));
|
||||
repo.Save(new AuditItem(i, AuditType.New, -1, UmbracoObjectTypes.Document.GetName(), $"Content {i} created"));
|
||||
repo.Save(new AuditItem(i, AuditType.Publish, -1, UmbracoObjectTypes.Document.GetName(), $"Content {i} published"));
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
@@ -122,10 +122,10 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
|
||||
using (var scope = sp.CreateScope())
|
||||
{
|
||||
var repo = new AuditRepository((IScopeAccessor) sp, Logger);
|
||||
var repo = new AuditRepository((IScopeAccessor)sp, Logger);
|
||||
|
||||
var page = repo.GetPagedResultsByQuery(sp.SqlContext.Query<IAuditItem>(), 0, 9, out var total, Direction.Descending,
|
||||
new[] {AuditType.Publish}, null)
|
||||
new[] { AuditType.Publish }, null)
|
||||
.ToArray();
|
||||
|
||||
Assert.AreEqual(9, page.Length);
|
||||
@@ -137,15 +137,15 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
[Test]
|
||||
public void Get_Paged_Items_With_Custom_Filter()
|
||||
{
|
||||
var sp = TestObjects.GetScopeProvider(Logger);
|
||||
var sp = ScopeProvider;
|
||||
using (var scope = sp.CreateScope())
|
||||
{
|
||||
var repo = new AuditRepository((IScopeAccessor) sp, Logger);
|
||||
var repo = new AuditRepository((IScopeAccessor)sp, Logger);
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
repo.Save(new AuditItem(i, AuditType.New, -1, ObjectTypes.GetName(UmbracoObjectTypes.Document), "Content created"));
|
||||
repo.Save(new AuditItem(i, AuditType.Publish, -1, ObjectTypes.GetName(UmbracoObjectTypes.Document), "Content published"));
|
||||
repo.Save(new AuditItem(i, AuditType.New, -1, UmbracoObjectTypes.Document.GetName(), "Content created"));
|
||||
repo.Save(new AuditItem(i, AuditType.Publish, -1, UmbracoObjectTypes.Document.GetName(), "Content published"));
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
@@ -153,7 +153,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
|
||||
using (var scope = sp.CreateScope())
|
||||
{
|
||||
var repo = new AuditRepository((IScopeAccessor) sp, Logger);
|
||||
var repo = new AuditRepository((IScopeAccessor)sp, Logger);
|
||||
|
||||
var page = repo.GetPagedResultsByQuery(sp.SqlContext.Query<IAuditItem>(), 0, 8, out var total, Direction.Descending,
|
||||
null, sp.SqlContext.Query<IAuditItem>().Where(item => item.Comment == "Content created"))
|
||||
@@ -0,0 +1,393 @@
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.Testing;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using System;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Services.Implement;
|
||||
using Umbraco.Tests.Integration.Testing;
|
||||
|
||||
namespace Umbraco.Tests.Persistence.Repositories
|
||||
{
|
||||
[TestFixture]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, WithApplication = true, Logger = UmbracoTestOptions.Logger.Console)]
|
||||
public class UserRepositoryTest : UmbracoIntegrationTest
|
||||
{
|
||||
private UserRepository CreateRepository(IScopeProvider provider)
|
||||
{
|
||||
var accessor = (IScopeAccessor) provider;
|
||||
var repository = new UserRepository(accessor, AppCaches.Disabled, Logger, Mappers, GlobalSettings, Mock.Of<IUserPasswordConfiguration>());
|
||||
return repository;
|
||||
}
|
||||
|
||||
private UserGroupRepository CreateUserGroupRepository(IScopeProvider provider)
|
||||
{
|
||||
var accessor = (IScopeAccessor) provider;
|
||||
return new UserGroupRepository(accessor, AppCaches.Disabled, Logger, ShortStringHelper);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Add_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = ScopeProvider;
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var user = UserBuilder.Build();
|
||||
|
||||
// Act
|
||||
repository.Save(user);
|
||||
|
||||
|
||||
// Assert
|
||||
Assert.That(user.HasIdentity, Is.True);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Multiple_Adds_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = ScopeProvider;
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var user1 = UserBuilder.WithSuffix("1").Build();
|
||||
var use2 = UserBuilder.WithSuffix("2").Build();
|
||||
|
||||
// Act
|
||||
repository.Save(user1);
|
||||
|
||||
repository.Save(use2);
|
||||
|
||||
|
||||
// Assert
|
||||
Assert.That(user1.HasIdentity, Is.True);
|
||||
Assert.That(use2.HasIdentity, Is.True);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Verify_Fresh_Entity_Is_Not_Dirty()
|
||||
{
|
||||
// Arrange
|
||||
var provider = ScopeProvider;
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var user = UserBuilder.Build();
|
||||
repository.Save(user);
|
||||
|
||||
|
||||
// Act
|
||||
var resolved = repository.Get((int)user.Id);
|
||||
bool dirty = ((User)resolved).IsDirty();
|
||||
|
||||
// Assert
|
||||
Assert.That(dirty, Is.False);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Delete_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = ScopeProvider;
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var user = UserBuilder.Build();
|
||||
|
||||
// Act
|
||||
repository.Save(user);
|
||||
|
||||
var id = user.Id;
|
||||
|
||||
var repository2 = new UserRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, Mock.Of<IMapperCollection>(),GlobalSettings, Mock.Of<IUserPasswordConfiguration>());
|
||||
|
||||
repository2.Delete(user);
|
||||
|
||||
|
||||
var resolved = repository2.Get((int) id);
|
||||
|
||||
// Assert
|
||||
Assert.That(resolved, Is.Null);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Get_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = ScopeProvider;
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
var userGroupRepository = CreateUserGroupRepository(provider);
|
||||
|
||||
var user = CreateAndCommitUserWithGroup(repository, userGroupRepository);
|
||||
|
||||
// Act
|
||||
var updatedItem = repository.Get(user.Id);
|
||||
|
||||
// FIXME: this test cannot work, user has 2 sections but the way it's created,
|
||||
// they don't show, so the comparison with updatedItem fails - fix!
|
||||
|
||||
// Assert
|
||||
AssertPropertyValues(updatedItem, user);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_GetByQuery_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = ScopeProvider;
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
CreateAndCommitMultipleUsers(repository);
|
||||
|
||||
// Act
|
||||
var query = scope.SqlContext.Query<IUser>().Where(x => x.Username == "TestUser1");
|
||||
var result = repository.Get(query);
|
||||
|
||||
// Assert
|
||||
Assert.That(result.Count(), Is.GreaterThanOrEqualTo(1));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_GetAll_By_Param_Ids_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = ScopeProvider;
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var users = CreateAndCommitMultipleUsers(repository);
|
||||
|
||||
// Act
|
||||
var result = repository.GetMany((int) users[0].Id, (int) users[1].Id);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result.Any(), Is.True);
|
||||
Assert.That(result.Count(), Is.EqualTo(2));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_GetAll_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = ScopeProvider;
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
CreateAndCommitMultipleUsers(repository);
|
||||
|
||||
// Act
|
||||
var result = repository.GetMany();
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result.Any(), Is.True);
|
||||
Assert.That(result.Count(), Is.GreaterThanOrEqualTo(3));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Exists_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = ScopeProvider;
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var users = CreateAndCommitMultipleUsers(repository);
|
||||
|
||||
// Act
|
||||
var exists = repository.Exists(users[0].Id);
|
||||
|
||||
// Assert
|
||||
Assert.That(exists, Is.True);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Count_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = ScopeProvider;
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var users = CreateAndCommitMultipleUsers(repository);
|
||||
|
||||
// Act
|
||||
var query = scope.SqlContext.Query<IUser>().Where(x => x.Username == "TestUser1" || x.Username == "TestUser2");
|
||||
var result = repository.Count(query);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2, result);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Get_Paged_Results_By_Query_And_Filter_And_Groups()
|
||||
{
|
||||
var provider = ScopeProvider;
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var users = CreateAndCommitMultipleUsers(repository);
|
||||
var query = provider.SqlContext.Query<IUser>().Where(x => x.Username == "TestUser1" || x.Username == "TestUser2");
|
||||
|
||||
try
|
||||
{
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlTrace = true;
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlCount = true;
|
||||
|
||||
// Act
|
||||
var result = repository.GetPagedResultsByQuery(query, 0, 10, out var totalRecs, user => user.Id, Direction.Ascending,
|
||||
excludeUserGroups: new[] { Constants.Security.TranslatorGroupAlias },
|
||||
filter: provider.SqlContext.Query<IUser>().Where(x => x.Id > -1));
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2, totalRecs);
|
||||
}
|
||||
finally
|
||||
{
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlTrace = false;
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlCount = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Get_Paged_Results_With_Filter_And_Groups()
|
||||
{
|
||||
var provider = ScopeProvider;
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var users = CreateAndCommitMultipleUsers(repository);
|
||||
|
||||
try
|
||||
{
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlTrace = true;
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlCount = true;
|
||||
|
||||
// Act
|
||||
var result = repository.GetPagedResultsByQuery(null, 0, 10, out var totalRecs, user => user.Id, Direction.Ascending,
|
||||
includeUserGroups: new[] { Constants.Security.AdminGroupAlias, Constants.Security.SensitiveDataGroupAlias },
|
||||
excludeUserGroups: new[] { Constants.Security.TranslatorGroupAlias },
|
||||
filter: provider.SqlContext.Query<IUser>().Where(x => x.Id == -1));
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(1, totalRecs);
|
||||
}
|
||||
finally
|
||||
{
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlTrace = false;
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlCount = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Invalidate_SecurityStamp_On_Username_Change()
|
||||
{
|
||||
// Arrange
|
||||
var provider = ScopeProvider;
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
var userGroupRepository = CreateUserGroupRepository(provider);
|
||||
|
||||
var user = CreateAndCommitUserWithGroup(repository, userGroupRepository);
|
||||
var originalSecurityStamp = user.SecurityStamp;
|
||||
|
||||
// Ensure when user generated a security stamp is present
|
||||
Assert.That(user.SecurityStamp, Is.Not.Null);
|
||||
Assert.That(user.SecurityStamp, Is.Not.Empty);
|
||||
|
||||
// Update username
|
||||
user.Username = user.Username + "UPDATED";
|
||||
repository.Save(user);
|
||||
|
||||
// Get the user
|
||||
var updatedUser = repository.Get(user.Id);
|
||||
|
||||
// Ensure the Security Stamp is invalidated & no longer the same
|
||||
Assert.AreNotEqual(originalSecurityStamp, updatedUser.SecurityStamp);
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertPropertyValues(IUser updatedItem, IUser originalUser)
|
||||
{
|
||||
Assert.That(updatedItem.Id, Is.EqualTo(originalUser.Id));
|
||||
Assert.That(updatedItem.Name, Is.EqualTo(originalUser.Name));
|
||||
Assert.That(updatedItem.Language, Is.EqualTo(originalUser.Language));
|
||||
Assert.That(updatedItem.IsApproved, Is.EqualTo(originalUser.IsApproved));
|
||||
Assert.That(updatedItem.RawPasswordValue, Is.EqualTo(originalUser.RawPasswordValue));
|
||||
Assert.That(updatedItem.IsLockedOut, Is.EqualTo(originalUser.IsLockedOut));
|
||||
Assert.IsTrue(updatedItem.StartContentIds.UnsortedSequenceEqual(originalUser.StartContentIds));
|
||||
Assert.IsTrue(updatedItem.StartMediaIds.UnsortedSequenceEqual(originalUser.StartMediaIds));
|
||||
Assert.That(updatedItem.Email, Is.EqualTo(originalUser.Email));
|
||||
Assert.That(updatedItem.Username, Is.EqualTo(originalUser.Username));
|
||||
Assert.That(updatedItem.AllowedSections.Count(), Is.EqualTo(originalUser.AllowedSections.Count()));
|
||||
foreach (var allowedSection in originalUser.AllowedSections)
|
||||
Assert.IsTrue(updatedItem.AllowedSections.Contains(allowedSection));
|
||||
}
|
||||
|
||||
private User CreateAndCommitUserWithGroup(IUserRepository repository, IUserGroupRepository userGroupRepository)
|
||||
{
|
||||
var user = UserBuilder.Build();
|
||||
repository.Save(user);
|
||||
|
||||
|
||||
var group = UserGroupBuilder.Build();
|
||||
userGroupRepository.AddOrUpdateGroupWithUsers(@group, new[] { user.Id });
|
||||
|
||||
user.AddGroup(UserGroupBuilder.BuildReadOnly(group));
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
private IUser[] CreateAndCommitMultipleUsers(IUserRepository repository)
|
||||
{
|
||||
var user1 = UserBuilder.WithSuffix("1").Build();
|
||||
var user2 = UserBuilder.WithSuffix("2").Build();
|
||||
var user3 = UserBuilder.WithSuffix("3").Build();
|
||||
repository.Save(user1);
|
||||
repository.Save(user2);
|
||||
repository.Save(user3);
|
||||
return new IUser[] { user1, user2, user3 };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,19 @@
|
||||
using LightInject;
|
||||
using LightInject.Microsoft.DependencyInjection;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Composing.LightInject;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Migrations.Install;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Runtime;
|
||||
using Umbraco.Tests.Common;
|
||||
using Umbraco.Tests.Integration.Extensions;
|
||||
using Umbraco.Tests.Integration.Implementations;
|
||||
using Umbraco.Tests.Integration.Testing;
|
||||
using Umbraco.Web.BackOffice.AspNetCore;
|
||||
using static Umbraco.Core.Migrations.Install.DatabaseBuilder;
|
||||
using Umbraco.Web.Common.Extensions;
|
||||
|
||||
namespace Umbraco.Tests.Integration
|
||||
{
|
||||
@@ -37,10 +28,11 @@ namespace Umbraco.Tests.Integration
|
||||
MyComposer.Reset();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void FixtureTearDown()
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
TestLocalDb.Cleanup();
|
||||
MyComponent.Reset();
|
||||
MyComposer.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -52,11 +44,11 @@ namespace Umbraco.Tests.Integration
|
||||
// LightInject / Umbraco
|
||||
var container = UmbracoServiceProviderFactory.CreateServiceContainer();
|
||||
var serviceProviderFactory = new UmbracoServiceProviderFactory(container);
|
||||
var umbracoContainer = serviceProviderFactory.GetContainer();
|
||||
var umbracoContainer = serviceProviderFactory.GetContainer();
|
||||
|
||||
// Special case since we are not using the Generic Host, we need to manually add an AspNetCore service to the container
|
||||
umbracoContainer.Register(x => Mock.Of<IHostApplicationLifetime>());
|
||||
|
||||
|
||||
var testHelper = new TestHelper();
|
||||
|
||||
// Create the core runtime
|
||||
@@ -70,7 +62,7 @@ namespace Umbraco.Tests.Integration
|
||||
|
||||
Assert.IsTrue(coreRuntime.MainDom.IsMainDom);
|
||||
Assert.IsNull(coreRuntime.State.BootFailedException);
|
||||
Assert.AreEqual(RuntimeLevel.Install, coreRuntime.State.Level);
|
||||
Assert.AreEqual(RuntimeLevel.Install, coreRuntime.State.Level);
|
||||
Assert.IsTrue(MyComposer.IsComposed);
|
||||
Assert.IsFalse(MyComponent.IsInit);
|
||||
Assert.IsFalse(MyComponent.IsTerminated);
|
||||
@@ -88,12 +80,12 @@ namespace Umbraco.Tests.Integration
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling AddUmbracoCore to configure the container
|
||||
/// Calling AddUmbracoCore to configure the container
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task AddUmbracoCore()
|
||||
{
|
||||
var umbracoContainer = GetUmbracoContainer(out var serviceProviderFactory);
|
||||
var umbracoContainer = UmbracoIntegrationTest.GetUmbracoContainer(out var serviceProviderFactory);
|
||||
var testHelper = new TestHelper();
|
||||
|
||||
var hostBuilder = new HostBuilder()
|
||||
@@ -101,7 +93,7 @@ namespace Umbraco.Tests.Integration
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
var webHostEnvironment = testHelper.GetWebHostEnvironment();
|
||||
AddRequiredNetCoreServices(services, testHelper, webHostEnvironment);
|
||||
services.AddRequiredNetCoreServices(testHelper, webHostEnvironment);
|
||||
|
||||
// Add it!
|
||||
services.AddUmbracoConfiguration(hostContext.Configuration);
|
||||
@@ -132,7 +124,7 @@ namespace Umbraco.Tests.Integration
|
||||
[Test]
|
||||
public async Task UseUmbracoCore()
|
||||
{
|
||||
var umbracoContainer = GetUmbracoContainer(out var serviceProviderFactory);
|
||||
var umbracoContainer = UmbracoIntegrationTest.GetUmbracoContainer(out var serviceProviderFactory);
|
||||
var testHelper = new TestHelper();
|
||||
|
||||
var hostBuilder = new HostBuilder()
|
||||
@@ -140,7 +132,7 @@ namespace Umbraco.Tests.Integration
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
var webHostEnvironment = testHelper.GetWebHostEnvironment();
|
||||
AddRequiredNetCoreServices(services, testHelper, webHostEnvironment);
|
||||
services.AddRequiredNetCoreServices(testHelper, webHostEnvironment);
|
||||
|
||||
// Add it!
|
||||
services.AddUmbracoConfiguration(hostContext.Configuration);
|
||||
@@ -167,71 +159,6 @@ namespace Umbraco.Tests.Integration
|
||||
Assert.IsTrue(MyComponent.IsTerminated);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Install_Database()
|
||||
{
|
||||
var umbracoContainer = GetUmbracoContainer(out var serviceProviderFactory);
|
||||
var testHelper = new TestHelper();
|
||||
|
||||
var hostBuilder = new HostBuilder()
|
||||
//TODO: Need to have a configured umb version for the runtime state
|
||||
.UseLocalDb(Path.Combine(testHelper.CurrentAssemblyDirectory, "LocalDb"))
|
||||
.UseUmbraco(serviceProviderFactory)
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
var webHostEnvironment = testHelper.GetWebHostEnvironment();
|
||||
AddRequiredNetCoreServices(services, testHelper, webHostEnvironment);
|
||||
|
||||
// Add it!
|
||||
services.AddUmbracoConfiguration(hostContext.Configuration);
|
||||
services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly);
|
||||
});
|
||||
|
||||
var host = await hostBuilder.StartAsync();
|
||||
var app = new ApplicationBuilder(host.Services);
|
||||
|
||||
app.UseUmbracoCore();
|
||||
|
||||
|
||||
var runtimeState = (RuntimeState)app.ApplicationServices.GetRequiredService<IRuntimeState>();
|
||||
Assert.AreEqual(RuntimeLevel.Install, runtimeState.Level);
|
||||
|
||||
var dbBuilder = app.ApplicationServices.GetRequiredService<DatabaseBuilder>();
|
||||
Assert.IsNotNull(dbBuilder);
|
||||
|
||||
var canConnect = dbBuilder.CanConnectToDatabase;
|
||||
Assert.IsTrue(canConnect);
|
||||
|
||||
var dbResult = dbBuilder.CreateSchemaAndData();
|
||||
Assert.IsTrue(dbResult.Success);
|
||||
|
||||
// TODO: Get this to work ... but to do that we need to mock or pass in a current umbraco version
|
||||
//var dbFactory = app.ApplicationServices.GetRequiredService<IUmbracoDatabaseFactory>();
|
||||
//var profilingLogger = app.ApplicationServices.GetRequiredService<IProfilingLogger>();
|
||||
//runtimeState.DetermineRuntimeLevel(dbFactory, profilingLogger);
|
||||
//Assert.AreEqual(RuntimeLevel.Run, runtimeState.Level);
|
||||
}
|
||||
|
||||
internal static LightInjectContainer GetUmbracoContainer(out UmbracoServiceProviderFactory serviceProviderFactory)
|
||||
{
|
||||
var container = UmbracoServiceProviderFactory.CreateServiceContainer();
|
||||
serviceProviderFactory = new UmbracoServiceProviderFactory(container);
|
||||
var umbracoContainer = serviceProviderFactory.GetContainer();
|
||||
return umbracoContainer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// These services need to be manually added because they do not get added by the generic host
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="testHelper"></param>
|
||||
/// <param name="webHostEnvironment"></param>
|
||||
private void AddRequiredNetCoreServices(IServiceCollection services, TestHelper testHelper, IWebHostEnvironment webHostEnvironment)
|
||||
{
|
||||
services.AddSingleton<IHttpContextAccessor>(x => testHelper.GetHttpContextAccessor());
|
||||
// the generic host does add IHostEnvironment but not this one because we are not actually in a web context
|
||||
services.AddSingleton<IWebHostEnvironment>(x => webHostEnvironment);
|
||||
}
|
||||
|
||||
[RuntimeLevel(MinLevel = RuntimeLevel.Install)]
|
||||
public class MyComposer : IUserComposer
|
||||
@@ -280,5 +207,5 @@ namespace Umbraco.Tests.Integration
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
326
src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs
Normal file
326
src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs
Normal file
@@ -0,0 +1,326 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Migrations.Install;
|
||||
using Umbraco.Core.Persistence;
|
||||
|
||||
namespace Umbraco.Tests.Integration.Testing
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages a pool of LocalDb databases for integration testing
|
||||
/// </summary>
|
||||
public class LocalDbTestDatabase
|
||||
{
|
||||
public const string InstanceName = "UmbracoTests";
|
||||
public const string DatabaseName = "UmbracoTests";
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly LocalDb _localDb;
|
||||
private readonly IUmbracoVersion _umbracoVersion;
|
||||
private static LocalDb.Instance _instance;
|
||||
private static string _filesPath;
|
||||
private readonly IUmbracoDatabaseFactory _dbFactory;
|
||||
private UmbracoDatabase.CommandInfo[] _dbCommands;
|
||||
private string _currentCstr;
|
||||
private static DatabasePool _emptyPool;
|
||||
private static DatabasePool _schemaPool;
|
||||
private DatabasePool _currentPool;
|
||||
|
||||
//It's internal because `Umbraco.Core.Persistence.LocalDb` is internal
|
||||
internal LocalDbTestDatabase(ILogger logger, IGlobalSettings globalSettings, LocalDb localDb, string filesPath, IUmbracoDatabaseFactory dbFactory)
|
||||
{
|
||||
_umbracoVersion = new UmbracoVersion();
|
||||
_logger = logger;
|
||||
_globalSettings = globalSettings;
|
||||
_localDb = localDb;
|
||||
_filesPath = filesPath;
|
||||
_dbFactory = dbFactory;
|
||||
|
||||
_instance = _localDb.GetInstance(InstanceName);
|
||||
if (_instance != null) return;
|
||||
|
||||
if (_localDb.CreateInstance(InstanceName) == false)
|
||||
throw new Exception("Failed to create a LocalDb instance.");
|
||||
_instance = _localDb.GetInstance(InstanceName);
|
||||
}
|
||||
|
||||
public string ConnectionString => _currentCstr ?? _instance.GetAttachedConnectionString("XXXXXX", _filesPath);
|
||||
|
||||
private void Create()
|
||||
{
|
||||
var tempName = Guid.NewGuid().ToString("N");
|
||||
_instance.CreateDatabase(tempName, _filesPath);
|
||||
_instance.DetachDatabase(tempName);
|
||||
|
||||
// there's probably a sweet spot to be found for size / parallel...
|
||||
|
||||
var s = ConfigurationManager.AppSettings["Umbraco.Tests.LocalDbTestDatabase.EmptyPoolSize"];
|
||||
var emptySize = s == null ? 2 : int.Parse(s);
|
||||
s = ConfigurationManager.AppSettings["Umbraco.Tests.LocalDbTestDatabase.EmptyPoolThreadCount"];
|
||||
var emptyParallel = s == null ? 1 : int.Parse(s);
|
||||
s = ConfigurationManager.AppSettings["Umbraco.Tests.LocalDbTestDatabase.SchemaPoolSize"];
|
||||
var schemaSize = s == null ? 2 : int.Parse(s);
|
||||
s = ConfigurationManager.AppSettings["Umbraco.Tests.LocalDbTestDatabase.SchemaPoolThreadCount"];
|
||||
var schemaParallel = s == null ? 1 : int.Parse(s);
|
||||
|
||||
_emptyPool = new DatabasePool(_localDb, _instance, DatabaseName + "-Empty", tempName, _filesPath, emptySize, emptyParallel);
|
||||
_schemaPool = new DatabasePool(_localDb, _instance, DatabaseName + "-Schema", tempName, _filesPath, schemaSize, schemaParallel, delete: true, prepare: RebuildSchema);
|
||||
}
|
||||
|
||||
public void AttachEmpty()
|
||||
{
|
||||
if (_emptyPool == null)
|
||||
Create();
|
||||
|
||||
_currentCstr = _emptyPool.AttachDatabase();
|
||||
_currentPool = _emptyPool;
|
||||
}
|
||||
|
||||
public void AttachSchema()
|
||||
{
|
||||
if (_schemaPool == null)
|
||||
Create();
|
||||
|
||||
_currentCstr = _schemaPool.AttachDatabase();
|
||||
_currentPool = _schemaPool;
|
||||
}
|
||||
|
||||
public void Detach()
|
||||
{
|
||||
_currentPool.DetachDatabase();
|
||||
}
|
||||
|
||||
private void RebuildSchema(DbConnection conn, IDbCommand cmd)
|
||||
{
|
||||
|
||||
if (_dbCommands != null)
|
||||
{
|
||||
foreach (var dbCommand in _dbCommands)
|
||||
{
|
||||
|
||||
if (dbCommand.Text.StartsWith("SELECT ")) continue;
|
||||
|
||||
cmd.CommandText = dbCommand.Text;
|
||||
cmd.Parameters.Clear();
|
||||
foreach (var parameterInfo in dbCommand.Parameters)
|
||||
AddParameter(cmd, parameterInfo);
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_dbFactory.Configure(conn.ConnectionString, Umbraco.Core.Constants.DatabaseProviders.SqlServer);
|
||||
|
||||
using var database = (UmbracoDatabase)_dbFactory.CreateDatabase();
|
||||
// track each db command ran as part of creating the database so we can replay these
|
||||
database.LogCommands = true;
|
||||
|
||||
using var trans = database.GetTransaction();
|
||||
|
||||
var creator = new DatabaseSchemaCreator(database, _logger, _umbracoVersion, _globalSettings);
|
||||
creator.InitializeDatabaseSchema();
|
||||
|
||||
trans.Complete(); // commit it
|
||||
|
||||
_dbCommands = database.Commands.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void AddParameter(IDbCommand cmd, UmbracoDatabase.ParameterInfo parameterInfo)
|
||||
{
|
||||
var p = cmd.CreateParameter();
|
||||
p.ParameterName = parameterInfo.Name;
|
||||
p.Value = parameterInfo.Value;
|
||||
p.DbType = parameterInfo.DbType;
|
||||
p.Size = parameterInfo.Size;
|
||||
cmd.Parameters.Add(p);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
var filename = Path.Combine(_filesPath, DatabaseName).ToUpper();
|
||||
|
||||
foreach (var database in _instance.GetDatabases())
|
||||
{
|
||||
if (database.StartsWith(filename))
|
||||
_instance.DropDatabase(database);
|
||||
}
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(_filesPath))
|
||||
{
|
||||
if (file.EndsWith(".mdf") == false && file.EndsWith(".ldf") == false) continue;
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ResetLocalDb(IDbCommand cmd)
|
||||
{
|
||||
// https://stackoverflow.com/questions/536350
|
||||
|
||||
cmd.CommandType = CommandType.Text;
|
||||
cmd.CommandText = @"
|
||||
declare @n char(1);
|
||||
set @n = char(10);
|
||||
declare @stmt nvarchar(max);
|
||||
-- check constraints
|
||||
select @stmt = isnull( @stmt + @n, '' ) +
|
||||
'alter table [' + schema_name(schema_id) + '].[' + object_name( parent_object_id ) + '] drop constraint [' + name + ']'
|
||||
from sys.check_constraints;
|
||||
-- foreign keys
|
||||
select @stmt = isnull( @stmt + @n, '' ) +
|
||||
'alter table [' + schema_name(schema_id) + '].[' + object_name( parent_object_id ) + '] drop constraint [' + name + ']'
|
||||
from sys.foreign_keys;
|
||||
-- tables
|
||||
select @stmt = isnull( @stmt + @n, '' ) +
|
||||
'drop table [' + schema_name(schema_id) + '].[' + name + ']'
|
||||
from sys.tables;
|
||||
exec sp_executesql @stmt;
|
||||
";
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
public static void KillLocalDb()
|
||||
{
|
||||
_emptyPool?.Stop();
|
||||
_schemaPool?.Stop();
|
||||
|
||||
if (_filesPath == null)
|
||||
return;
|
||||
|
||||
var filename = Path.Combine(_filesPath, DatabaseName).ToUpper();
|
||||
|
||||
foreach (var database in _instance.GetDatabases())
|
||||
{
|
||||
if (database.StartsWith(filename))
|
||||
_instance.DropDatabase(database);
|
||||
}
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(_filesPath))
|
||||
{
|
||||
if (file.EndsWith(".mdf") == false && file.EndsWith(".ldf") == false) continue;
|
||||
try
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// ignore, must still be in use but nothing we can do
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DatabasePool
|
||||
{
|
||||
private readonly LocalDb _localDb;
|
||||
private readonly LocalDb.Instance _instance;
|
||||
private readonly string _filesPath;
|
||||
private readonly string _name;
|
||||
private readonly int _size;
|
||||
private readonly string[] _cstrs;
|
||||
private readonly BlockingCollection<int> _prepareQueue, _readyQueue;
|
||||
private readonly Action<DbConnection, IDbCommand> _prepare;
|
||||
private int _current;
|
||||
|
||||
public DatabasePool(LocalDb localDb, LocalDb.Instance instance, string name, string tempName, string filesPath, int size, int parallel = 1, Action<DbConnection, IDbCommand> prepare = null, bool delete = false)
|
||||
{
|
||||
_localDb = localDb;
|
||||
_instance = instance;
|
||||
_filesPath = filesPath;
|
||||
_name = name;
|
||||
_size = size;
|
||||
_prepare = prepare;
|
||||
_prepareQueue = new BlockingCollection<int>();
|
||||
_readyQueue = new BlockingCollection<int>();
|
||||
_cstrs = new string[_size];
|
||||
|
||||
for (var i = 0; i < size; i++)
|
||||
localDb.CopyDatabaseFiles(tempName, filesPath, targetDatabaseName: name + "-" + i, overwrite: true, delete: delete && i == size - 1);
|
||||
|
||||
if (prepare == null)
|
||||
{
|
||||
for (var i = 0; i < size; i++)
|
||||
_readyQueue.Add(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < size; i++)
|
||||
_prepareQueue.Add(i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < parallel; i++)
|
||||
{
|
||||
var thread = new Thread(PrepareThread);
|
||||
thread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public string AttachDatabase()
|
||||
{
|
||||
try
|
||||
{
|
||||
_current = _readyQueue.Take();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
_current = 0;
|
||||
return null;
|
||||
}
|
||||
return ConnectionString(_current);
|
||||
}
|
||||
|
||||
public void DetachDatabase()
|
||||
{
|
||||
_prepareQueue.Add(_current);
|
||||
}
|
||||
|
||||
private string ConnectionString(int i)
|
||||
{
|
||||
return _cstrs[i] ?? (_cstrs[i] = _instance.GetAttachedConnectionString(_name + "-" + i, _filesPath));
|
||||
}
|
||||
|
||||
private void PrepareThread()
|
||||
{
|
||||
while (_prepareQueue.IsCompleted == false)
|
||||
{
|
||||
int i;
|
||||
try
|
||||
{
|
||||
i = _prepareQueue.Take();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
using (var conn = new SqlConnection(ConnectionString(i)))
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
conn.Open();
|
||||
ResetLocalDb(cmd);
|
||||
_prepare?.Invoke(conn, cmd);
|
||||
}
|
||||
_readyQueue.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
int i;
|
||||
_prepareQueue.CompleteAdding();
|
||||
while (_prepareQueue.TryTake(out i)) { }
|
||||
_readyQueue.CompleteAdding();
|
||||
while (_readyQueue.TryTake(out i)) { }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Umbraco.Core.Persistence;
|
||||
|
||||
namespace Umbraco.Tests.Integration.Testing
|
||||
{
|
||||
public static class TestLocalDb
|
||||
{
|
||||
private const string LocalDbInstanceName = "UmbTests";
|
||||
|
||||
private static LocalDb LocalDb { get; } = new LocalDb();
|
||||
|
||||
// TODO: We need to borrow logic from this old branch, this is the latest commit at the old branch where we had LocalDb
|
||||
// working for tests. There's a lot of hoops to jump through to make it work 'fast'. Turns out it didn't actually run as
|
||||
// fast as SqlCe due to the dropping/creating of DB instances since that is faster in SqlCe but this code was all heavily
|
||||
// optimized to go as fast as possible.
|
||||
// see https://github.com/umbraco/Umbraco-CMS/blob/3a8716ac7b1c48b51258724337086cd0712625a1/src/Umbraco.Tests/TestHelpers/LocalDbTestDatabase.cs
|
||||
internal static LocalDb.Instance EnsureLocalDbInstanceAndDatabase(string dbName, string dbFilePath)
|
||||
{
|
||||
if (!LocalDb.InstanceExists(LocalDbInstanceName) && !LocalDb.CreateInstance(LocalDbInstanceName))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Failed to create LocalDb instance {LocalDbInstanceName}, assuming LocalDb is not really available.");
|
||||
}
|
||||
|
||||
var instance = LocalDb.GetInstance(LocalDbInstanceName);
|
||||
|
||||
if (instance == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Failed to get LocalDb instance {LocalDbInstanceName}, assuming LocalDb is not really available.");
|
||||
}
|
||||
|
||||
instance.CreateDatabase(dbName, dbFilePath);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static void Cleanup()
|
||||
{
|
||||
var instance = LocalDb.GetInstance(LocalDbInstanceName);
|
||||
if (instance != null)
|
||||
{
|
||||
instance.DropDatabases();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
172
src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs
Normal file
172
src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Composing.LightInject;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Strings;
|
||||
using Umbraco.Tests.Common.Builders;
|
||||
using Umbraco.Tests.Integration.Extensions;
|
||||
using Umbraco.Tests.Integration.Implementations;
|
||||
using Umbraco.Web.BackOffice.AspNetCore;
|
||||
using Umbraco.Web.Common.Extensions;
|
||||
|
||||
namespace Umbraco.Tests.Integration.Testing
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract class for integration tests
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will use a Host Builder to boot and install Umbraco ready for use
|
||||
/// </remarks>
|
||||
[SingleThreaded]
|
||||
[NonParallelizable]
|
||||
public abstract class UmbracoIntegrationTest
|
||||
{
|
||||
public static LightInjectContainer GetUmbracoContainer(out UmbracoServiceProviderFactory serviceProviderFactory)
|
||||
{
|
||||
var container = UmbracoServiceProviderFactory.CreateServiceContainer();
|
||||
serviceProviderFactory = new UmbracoServiceProviderFactory(container);
|
||||
var umbracoContainer = serviceProviderFactory.GetContainer();
|
||||
return umbracoContainer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or create an instance of <see cref="LocalDbTestDatabase"/>
|
||||
/// </summary>
|
||||
/// <param name="filesPath"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="globalSettings"></param>
|
||||
/// <param name="dbFactory"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// There must only be ONE instance shared between all tests in a session
|
||||
/// </remarks>
|
||||
public static LocalDbTestDatabase GetOrCreate(string filesPath, ILogger logger, IGlobalSettings globalSettings, IUmbracoDatabaseFactory dbFactory)
|
||||
{
|
||||
lock (_dbLocker)
|
||||
{
|
||||
if (_dbInstance != null) return _dbInstance;
|
||||
|
||||
var localDb = new LocalDb();
|
||||
if (localDb.IsAvailable == false)
|
||||
throw new InvalidOperationException("LocalDB is not available.");
|
||||
_dbInstance = new LocalDbTestDatabase(logger, globalSettings, localDb, filesPath, dbFactory);
|
||||
return _dbInstance;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly object _dbLocker = new object();
|
||||
private static LocalDbTestDatabase _dbInstance;
|
||||
|
||||
private readonly List<Action> _testTeardown = new List<Action>();
|
||||
private readonly List<Action> _fixtureTeardown = new List<Action>();
|
||||
|
||||
public void OnTestTearDown(Action tearDown)
|
||||
{
|
||||
_testTeardown.Add(tearDown);
|
||||
}
|
||||
|
||||
public void OnFixtureTearDown(Action tearDown)
|
||||
{
|
||||
_fixtureTeardown.Add(tearDown);
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void FixtureTearDown()
|
||||
{
|
||||
// call all registered callbacks
|
||||
foreach (var action in _fixtureTeardown)
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
// call all registered callbacks
|
||||
foreach (var action in _testTeardown)
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
var umbracoContainer = GetUmbracoContainer(out var serviceProviderFactory);
|
||||
var testHelper = new TestHelper();
|
||||
|
||||
var hostBuilder = new HostBuilder()
|
||||
.UseUmbraco(serviceProviderFactory)
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
var webHostEnvironment = testHelper.GetWebHostEnvironment();
|
||||
services.AddRequiredNetCoreServices(testHelper, webHostEnvironment);
|
||||
|
||||
// Add it!
|
||||
services.AddUmbracoConfiguration(hostContext.Configuration);
|
||||
services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly);
|
||||
});
|
||||
|
||||
var host = await hostBuilder.StartAsync();
|
||||
var app = new ApplicationBuilder(host.Services);
|
||||
Services = app.ApplicationServices;
|
||||
|
||||
// This will create a db, install the schema and ensure the app is configured to run
|
||||
app.UseTestLocalDb(Path.Combine(testHelper.CurrentAssemblyDirectory, "LocalDb"), this);
|
||||
|
||||
app.UseUmbracoCore();
|
||||
}
|
||||
|
||||
#region Common services
|
||||
|
||||
/// <summary>
|
||||
/// Returns the DI container
|
||||
/// </summary>
|
||||
protected IServiceProvider Services { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="IScopeProvider"/>
|
||||
/// </summary>
|
||||
protected IScopeProvider ScopeProvider => Services.GetRequiredService<IScopeProvider>();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="IScopeAccessor"/>
|
||||
/// </summary>
|
||||
protected IScopeAccessor ScopeAccessor => Services.GetRequiredService<IScopeAccessor>();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="ILogger"/>
|
||||
/// </summary>
|
||||
protected ILogger Logger => Services.GetRequiredService<ILogger>();
|
||||
|
||||
protected AppCaches AppCaches => Services.GetRequiredService<AppCaches>();
|
||||
protected IIOHelper IOHelper => Services.GetRequiredService<IIOHelper>();
|
||||
protected IShortStringHelper ShortStringHelper => Services.GetRequiredService<IShortStringHelper>();
|
||||
protected IGlobalSettings GlobalSettings => Services.GetRequiredService<IGlobalSettings>();
|
||||
protected IMapperCollection Mappers => Services.GetRequiredService<IMapperCollection>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Builders
|
||||
|
||||
protected GlobalSettingsBuilder GlobalSettingsBuilder = new GlobalSettingsBuilder();
|
||||
protected UserBuilder UserBuilder = new UserBuilder();
|
||||
protected UserGroupBuilder UserGroupBuilder = new UserGroupBuilder();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<LangVersion>8</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -16,10 +16,10 @@ using Umbraco.Core.PropertyEditors.ValueConverters;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.Components;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
using System.Text;
|
||||
using Umbraco.Infrastructure.Media;
|
||||
|
||||
namespace Umbraco.Tests.Models
|
||||
{
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace Umbraco.Tests.Persistence
|
||||
}
|
||||
|
||||
// re-create the database factory and database context with proper connection string
|
||||
_databaseFactory = new UmbracoDatabaseFactory(connString, Constants.DbProviderNames.SqlCe, _logger, new Lazy<IMapperCollection>(() => Mock.Of<IMapperCollection>()), TestHelper.DbProviderFactoryCreator);
|
||||
_databaseFactory = new UmbracoDatabaseFactory(_logger, connString, Constants.DbProviderNames.SqlCe, new Lazy<IMapperCollection>(() => Mock.Of<IMapperCollection>()), TestHelper.DbProviderFactoryCreator);
|
||||
|
||||
// test get database type (requires an actual database)
|
||||
using (var database = _databaseFactory.CreateDatabase())
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Umbraco.Tests.Persistence.FaultHandling
|
||||
{
|
||||
const string connectionString = @"server=.\SQLEXPRESS;database=EmptyForTest;user id=x;password=umbraco";
|
||||
const string providerName = Constants.DbProviderNames.SqlServer;
|
||||
var factory = new UmbracoDatabaseFactory(connectionString, providerName, Mock.Of<ILogger>(), new Lazy<IMapperCollection>(() => Mock.Of<IMapperCollection>()), TestHelper.DbProviderFactoryCreator);
|
||||
var factory = new UmbracoDatabaseFactory(Mock.Of<ILogger>(), connectionString, providerName, new Lazy<IMapperCollection>(() => Mock.Of<IMapperCollection>()), TestHelper.DbProviderFactoryCreator);
|
||||
|
||||
using (var database = factory.CreateDatabase())
|
||||
{
|
||||
@@ -34,7 +34,7 @@ namespace Umbraco.Tests.Persistence.FaultHandling
|
||||
{
|
||||
const string connectionString = @"server=.\SQLEXPRESS;database=EmptyForTest;user id=umbraco;password=umbraco";
|
||||
const string providerName = Constants.DbProviderNames.SqlServer;
|
||||
var factory = new UmbracoDatabaseFactory(connectionString, providerName, Mock.Of<ILogger>(), new Lazy<IMapperCollection>(() => Mock.Of<IMapperCollection>()), TestHelper.DbProviderFactoryCreator);
|
||||
var factory = new UmbracoDatabaseFactory(Mock.Of<ILogger>(), connectionString, providerName, new Lazy<IMapperCollection>(() => Mock.Of<IMapperCollection>()), TestHelper.DbProviderFactoryCreator);
|
||||
|
||||
using (var database = factory.CreateDatabase())
|
||||
{
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.Testing;
|
||||
|
||||
namespace Umbraco.Tests.Persistence.Repositories
|
||||
{
|
||||
[TestFixture]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
|
||||
public class KeyValueRepositoryTests : TestWithDatabaseBase
|
||||
{
|
||||
[Test]
|
||||
public void CanSetAndGet()
|
||||
{
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
|
||||
// Insert new key/value
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var keyValue = new KeyValue
|
||||
{
|
||||
Identifier = "foo",
|
||||
Value = "bar",
|
||||
UpdateDate = DateTime.Now,
|
||||
};
|
||||
var repo = CreateRepository(provider);
|
||||
repo.Save(keyValue);
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
// Retrieve key/value
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repo = CreateRepository(provider);
|
||||
var keyValue = repo.Get("foo");
|
||||
scope.Complete();
|
||||
|
||||
Assert.AreEqual("bar", keyValue.Value);
|
||||
}
|
||||
|
||||
// Update value
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repo = CreateRepository(provider);
|
||||
var keyValue = repo.Get("foo");
|
||||
keyValue.Value = "buzz";
|
||||
keyValue.UpdateDate = DateTime.Now;
|
||||
repo.Save(keyValue);
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
// Retrieve key/value again
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repo = CreateRepository(provider);
|
||||
var keyValue = repo.Get("foo");
|
||||
scope.Complete();
|
||||
|
||||
Assert.AreEqual("buzz", keyValue.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private IKeyValueRepository CreateRepository(IScopeProvider provider)
|
||||
{
|
||||
return new KeyValueRepository((IScopeAccessor) provider, Logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,21 +5,20 @@ using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
using Umbraco.Tests.Testing;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using System;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Tests.Persistence.Repositories
|
||||
{
|
||||
// TODO: Move the remaining parts to Integration tests
|
||||
|
||||
[TestFixture]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, WithApplication = true, Logger = UmbracoTestOptions.Logger.Console)]
|
||||
public class UserRepositoryTest : TestWithDatabaseBase
|
||||
@@ -78,72 +77,6 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
return new UserGroupRepository(accessor, AppCaches.Disabled, Logger, ShortStringHelper);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Add_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var user = MockedUser.CreateUser();
|
||||
|
||||
// Act
|
||||
repository.Save(user);
|
||||
|
||||
|
||||
// Assert
|
||||
Assert.That(user.HasIdentity, Is.True);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Multiple_Adds_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var user1 = MockedUser.CreateUser("1");
|
||||
var use2 = MockedUser.CreateUser("2");
|
||||
|
||||
// Act
|
||||
repository.Save(user1);
|
||||
|
||||
repository.Save(use2);
|
||||
|
||||
|
||||
// Assert
|
||||
Assert.That(user1.HasIdentity, Is.True);
|
||||
Assert.That(use2.HasIdentity, Is.True);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Verify_Fresh_Entity_Is_Not_Dirty()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var user = MockedUser.CreateUser();
|
||||
repository.Save(user);
|
||||
|
||||
|
||||
// Act
|
||||
var resolved = repository.Get((int)user.Id);
|
||||
bool dirty = ((User)resolved).IsDirty();
|
||||
|
||||
// Assert
|
||||
Assert.That(dirty, Is.False);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Update_On_UserRepository()
|
||||
{
|
||||
@@ -206,268 +139,6 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Delete_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var user = MockedUser.CreateUser();
|
||||
|
||||
// Act
|
||||
repository.Save(user);
|
||||
|
||||
var id = user.Id;
|
||||
|
||||
var repository2 = new UserRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, Mock.Of<IMapperCollection>(),TestObjects.GetGlobalSettings(), Mock.Of<IUserPasswordConfiguration>());
|
||||
|
||||
repository2.Delete(user);
|
||||
|
||||
|
||||
var resolved = repository2.Get((int) id);
|
||||
|
||||
// Assert
|
||||
Assert.That(resolved, Is.Null);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Get_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
var userGroupRepository = CreateUserGroupRepository(provider);
|
||||
|
||||
var user = CreateAndCommitUserWithGroup(repository, userGroupRepository);
|
||||
|
||||
// Act
|
||||
var updatedItem = repository.Get(user.Id);
|
||||
|
||||
// FIXME: this test cannot work, user has 2 sections but the way it's created,
|
||||
// they don't show, so the comparison with updatedItem fails - fix!
|
||||
|
||||
// Assert
|
||||
AssertPropertyValues(updatedItem, user);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_GetByQuery_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
CreateAndCommitMultipleUsers(repository);
|
||||
|
||||
// Act
|
||||
var query = scope.SqlContext.Query<IUser>().Where(x => x.Username == "TestUser1");
|
||||
var result = repository.Get(query);
|
||||
|
||||
// Assert
|
||||
Assert.That(result.Count(), Is.GreaterThanOrEqualTo(1));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_GetAll_By_Param_Ids_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var users = CreateAndCommitMultipleUsers(repository);
|
||||
|
||||
// Act
|
||||
var result = repository.GetMany((int) users[0].Id, (int) users[1].Id);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result.Any(), Is.True);
|
||||
Assert.That(result.Count(), Is.EqualTo(2));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_GetAll_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
CreateAndCommitMultipleUsers(repository);
|
||||
|
||||
// Act
|
||||
var result = repository.GetMany();
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result.Any(), Is.True);
|
||||
Assert.That(result.Count(), Is.GreaterThanOrEqualTo(3));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Exists_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var users = CreateAndCommitMultipleUsers(repository);
|
||||
|
||||
// Act
|
||||
var exists = repository.Exists(users[0].Id);
|
||||
|
||||
// Assert
|
||||
Assert.That(exists, Is.True);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Count_On_UserRepository()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var users = CreateAndCommitMultipleUsers(repository);
|
||||
|
||||
// Act
|
||||
var query = scope.SqlContext.Query<IUser>().Where(x => x.Username == "TestUser1" || x.Username == "TestUser2");
|
||||
var result = repository.Count(query);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2, result);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Get_Paged_Results_By_Query_And_Filter_And_Groups()
|
||||
{
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var users = CreateAndCommitMultipleUsers(repository);
|
||||
var query = provider.SqlContext.Query<IUser>().Where(x => x.Username == "TestUser1" || x.Username == "TestUser2");
|
||||
|
||||
try
|
||||
{
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlTrace = true;
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlCount = true;
|
||||
|
||||
// Act
|
||||
var result = repository.GetPagedResultsByQuery(query, 0, 10, out var totalRecs, user => user.Id, Direction.Ascending,
|
||||
excludeUserGroups: new[] { Constants.Security.TranslatorGroupAlias },
|
||||
filter: provider.SqlContext.Query<IUser>().Where(x => x.Id > -1));
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2, totalRecs);
|
||||
}
|
||||
finally
|
||||
{
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlTrace = false;
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlCount = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Get_Paged_Results_With_Filter_And_Groups()
|
||||
{
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
var users = CreateAndCommitMultipleUsers(repository);
|
||||
|
||||
try
|
||||
{
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlTrace = true;
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlCount = true;
|
||||
|
||||
// Act
|
||||
var result = repository.GetPagedResultsByQuery(null, 0, 10, out var totalRecs, user => user.Id, Direction.Ascending,
|
||||
includeUserGroups: new[] { Constants.Security.AdminGroupAlias, Constants.Security.SensitiveDataGroupAlias },
|
||||
excludeUserGroups: new[] { Constants.Security.TranslatorGroupAlias },
|
||||
filter: provider.SqlContext.Query<IUser>().Where(x => x.Id == -1));
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(1, totalRecs);
|
||||
}
|
||||
finally
|
||||
{
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlTrace = false;
|
||||
scope.Database.AsUmbracoDatabase().EnableSqlCount = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Invalidate_SecurityStamp_On_Username_Change()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider);
|
||||
var userGroupRepository = CreateUserGroupRepository(provider);
|
||||
|
||||
var user = CreateAndCommitUserWithGroup(repository, userGroupRepository);
|
||||
var originalSecurityStamp = user.SecurityStamp;
|
||||
|
||||
// Ensure when user generated a security stamp is present
|
||||
Assert.That(user.SecurityStamp, Is.Not.Null);
|
||||
Assert.That(user.SecurityStamp, Is.Not.Empty);
|
||||
|
||||
// Update username
|
||||
user.Username = user.Username + "UPDATED";
|
||||
repository.Save(user);
|
||||
|
||||
// Get the user
|
||||
var updatedUser = repository.Get(user.Id);
|
||||
|
||||
// Ensure the Security Stamp is invalidated & no longer the same
|
||||
Assert.AreNotEqual(originalSecurityStamp, updatedUser.SecurityStamp);
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertPropertyValues(IUser updatedItem, IUser originalUser)
|
||||
{
|
||||
Assert.That(updatedItem.Id, Is.EqualTo(originalUser.Id));
|
||||
Assert.That(updatedItem.Name, Is.EqualTo(originalUser.Name));
|
||||
Assert.That(updatedItem.Language, Is.EqualTo(originalUser.Language));
|
||||
Assert.That(updatedItem.IsApproved, Is.EqualTo(originalUser.IsApproved));
|
||||
Assert.That(updatedItem.RawPasswordValue, Is.EqualTo(originalUser.RawPasswordValue));
|
||||
Assert.That(updatedItem.IsLockedOut, Is.EqualTo(originalUser.IsLockedOut));
|
||||
Assert.IsTrue(updatedItem.StartContentIds.UnsortedSequenceEqual(originalUser.StartContentIds));
|
||||
Assert.IsTrue(updatedItem.StartMediaIds.UnsortedSequenceEqual(originalUser.StartMediaIds));
|
||||
Assert.That(updatedItem.Email, Is.EqualTo(originalUser.Email));
|
||||
Assert.That(updatedItem.Username, Is.EqualTo(originalUser.Username));
|
||||
Assert.That(updatedItem.AllowedSections.Count(), Is.EqualTo(originalUser.AllowedSections.Count()));
|
||||
foreach (var allowedSection in originalUser.AllowedSections)
|
||||
Assert.IsTrue(updatedItem.AllowedSections.Contains(allowedSection));
|
||||
}
|
||||
|
||||
private static User CreateAndCommitUserWithGroup(IUserRepository repository, IUserGroupRepository userGroupRepository)
|
||||
{
|
||||
@@ -483,15 +154,5 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
return user;
|
||||
}
|
||||
|
||||
private IUser[] CreateAndCommitMultipleUsers(IUserRepository repository)
|
||||
{
|
||||
var user1 = MockedUser.CreateUser("1");
|
||||
var user2 = MockedUser.CreateUser("2");
|
||||
var user3 = MockedUser.CreateUser("3");
|
||||
repository.Save(user1);
|
||||
repository.Save(user2);
|
||||
repository.Save(user3);
|
||||
return new IUser[] { user1, user2, user3 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ using Umbraco.Web.PropertyEditors;
|
||||
using System.Text;
|
||||
using Current = Umbraco.Web.Composing.Current;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Tests.PropertyEditors
|
||||
{
|
||||
|
||||
@@ -16,6 +16,7 @@ using Umbraco.Web;
|
||||
using Umbraco.Web.Templates;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Tests.PublishedContent
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@ using Umbraco.Web.Templates;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Routing;
|
||||
using Current = Umbraco.Web.Composing.Current;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Tests.PublishedContent
|
||||
{
|
||||
|
||||
@@ -36,6 +36,7 @@ using File = System.IO.File;
|
||||
using Current = Umbraco.Web.Composing.Current;
|
||||
using Umbraco.Tests.Common;
|
||||
using Umbraco.Tests.Common.Composing;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Tests.Runtimes
|
||||
{
|
||||
|
||||
93
src/Umbraco.Tests/Services/KeyValueServiceTests.cs
Normal file
93
src/Umbraco.Tests/Services/KeyValueServiceTests.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System.Threading;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Internal;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.Testing;
|
||||
|
||||
namespace Umbraco.Tests.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests covering methods in the KeyValueService class.
|
||||
/// This is more of an integration test as it involves multiple layers
|
||||
/// as well as configuration.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
[Apartment(ApartmentState.STA)]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
|
||||
public class KeyValueServiceTests : TestWithSomeContentBase
|
||||
{
|
||||
[Test]
|
||||
public void GetValue_ForMissingKey_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var keyValueService = ServiceContext.KeyValueService;
|
||||
|
||||
// Act
|
||||
var value = keyValueService.GetValue("foo");
|
||||
|
||||
// Assert
|
||||
Assert.IsNull(value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetValue_ForExistingKey_ReturnsValue()
|
||||
{
|
||||
// Arrange
|
||||
var keyValueService = ServiceContext.KeyValueService;
|
||||
keyValueService.SetValue("foo", "bar");
|
||||
|
||||
// Act
|
||||
var value = keyValueService.GetValue("foo");
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("bar", value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetValue_ForExistingKey_SavesValue()
|
||||
{
|
||||
// Arrange
|
||||
var keyValueService = ServiceContext.KeyValueService;
|
||||
keyValueService.SetValue("foo", "bar");
|
||||
|
||||
// Act
|
||||
keyValueService.SetValue("foo", "buzz");
|
||||
var value = keyValueService.GetValue("foo");
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("buzz", value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TrySetValue_ForExistingKeyWithProvidedValue_ReturnsTrueAndSetsValue()
|
||||
{
|
||||
// Arrange
|
||||
var keyValueService = ServiceContext.KeyValueService;
|
||||
keyValueService.SetValue("foo", "bar");
|
||||
|
||||
// Act
|
||||
var result = keyValueService.TrySetValue("foo", "bar", "buzz");
|
||||
var value = keyValueService.GetValue("foo");
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(result);
|
||||
Assert.AreEqual("buzz", value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TrySetValue_ForExistingKeyWithoutProvidedValue_ReturnsFalseAndDoesNotSetValue()
|
||||
{
|
||||
// Arrange
|
||||
var keyValueService = ServiceContext.KeyValueService;
|
||||
keyValueService.SetValue("foo", "bar");
|
||||
|
||||
// Act
|
||||
var result = keyValueService.TrySetValue("foo", "bang", "buzz");
|
||||
var value = keyValueService.GetValue("foo");
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(result);
|
||||
Assert.AreEqual("bar", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,24 +4,19 @@ using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Web;
|
||||
using Moq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.Common;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.Security;
|
||||
|
||||
namespace Umbraco.Tests.TestHelpers
|
||||
{
|
||||
|
||||
@@ -193,6 +193,7 @@ namespace Umbraco.Tests.TestHelpers
|
||||
var tagService = GetLazyService<ITagService>(factory, c => new TagService(scopeProvider, logger, eventMessagesFactory, GetRepo<ITagRepository>(c)));
|
||||
var redirectUrlService = GetLazyService<IRedirectUrlService>(factory, c => new RedirectUrlService(scopeProvider, logger, eventMessagesFactory, GetRepo<IRedirectUrlRepository>(c)));
|
||||
var consentService = GetLazyService<IConsentService>(factory, c => new ConsentService(scopeProvider, logger, eventMessagesFactory, GetRepo<IConsentRepository>(c)));
|
||||
var keyValueService = GetLazyService<IKeyValueService>(factory, c => new KeyValueService(scopeProvider, GetRepo<IKeyValueRepository>(c)));
|
||||
var contentTypeServiceBaseFactory = GetLazyService<IContentTypeBaseServiceProvider>(factory, c => new ContentTypeBaseServiceProvider(factory.GetInstance<IContentTypeService>(),factory.GetInstance<IMediaTypeService>(),factory.GetInstance<IMemberTypeService>()));
|
||||
|
||||
return new ServiceContext(
|
||||
@@ -221,6 +222,7 @@ namespace Umbraco.Tests.TestHelpers
|
||||
externalLoginService,
|
||||
redirectUrlService,
|
||||
consentService,
|
||||
keyValueService,
|
||||
contentTypeServiceBaseFactory);
|
||||
}
|
||||
|
||||
@@ -244,17 +246,16 @@ namespace Umbraco.Tests.TestHelpers
|
||||
// mappersBuilder.AddCore();
|
||||
// var mappers = mappersBuilder.CreateCollection();
|
||||
var mappers = Current.Factory.GetInstance<IMapperCollection>();
|
||||
databaseFactory = new UmbracoDatabaseFactory(
|
||||
Constants.System.UmbracoConnectionName,
|
||||
databaseFactory = new UmbracoDatabaseFactory(logger,
|
||||
SettingsForTests.GetDefaultGlobalSettings(),
|
||||
new ConnectionStrings(),
|
||||
logger,
|
||||
Constants.System.UmbracoConnectionName,
|
||||
new Lazy<IMapperCollection>(() => mappers),
|
||||
TestHelper.DbProviderFactoryCreator);
|
||||
}
|
||||
|
||||
typeFinder = typeFinder ?? new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly));
|
||||
fileSystems = fileSystems ?? new FileSystems(Current.Factory, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings());
|
||||
typeFinder ??= new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly));
|
||||
fileSystems ??= new FileSystems(Current.Factory, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings());
|
||||
var coreDebug = TestHelper.CoreDebugSettings;
|
||||
var mediaFileSystem = Mock.Of<IMediaFileSystem>();
|
||||
var scopeProvider = new ScopeProvider(databaseFactory, fileSystems, coreDebug, mediaFileSystem, logger, typeFinder, NoAppCache.Instance);
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace Umbraco.Tests.TestHelpers
|
||||
return TestObjects.GetDatabaseFactoryMock();
|
||||
|
||||
var lazyMappers = new Lazy<IMapperCollection>(f.GetInstance<IMapperCollection>);
|
||||
var factory = new UmbracoDatabaseFactory(GetDbConnectionString(), GetDbProviderName(), f.GetInstance<ILogger>(), lazyMappers, TestHelper.DbProviderFactoryCreator);
|
||||
var factory = new UmbracoDatabaseFactory(f.GetInstance<ILogger>(), GetDbConnectionString(), GetDbProviderName(), lazyMappers, TestHelper.DbProviderFactoryCreator);
|
||||
factory.ResetForTests();
|
||||
return factory;
|
||||
});
|
||||
|
||||
@@ -50,7 +50,6 @@ using FileSystems = Umbraco.Core.IO.FileSystems;
|
||||
using Umbraco.Web.Templates;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
using Umbraco.Core.Dictionary;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Net;
|
||||
using Umbraco.Core.Request;
|
||||
using Umbraco.Core.Security;
|
||||
@@ -63,6 +62,7 @@ using Umbraco.Web.Security.Providers;
|
||||
using Umbraco.Web.Trees;
|
||||
using Current = Umbraco.Web.Composing.Current;
|
||||
using Umbraco.Tests.Common;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Tests.Testing
|
||||
{
|
||||
@@ -462,13 +462,13 @@ namespace Umbraco.Tests.Testing
|
||||
var globalSettings = TestHelper.GetConfigs().Global();
|
||||
var connectionStrings = TestHelper.GetConfigs().ConnectionStrings();
|
||||
|
||||
Composition.RegisterUnique<IUmbracoDatabaseFactory>(f => new UmbracoDatabaseFactory(
|
||||
Constants.System.UmbracoConnectionName,
|
||||
Composition.RegisterUnique<IUmbracoDatabaseFactory>(f => new UmbracoDatabaseFactory(Logger,
|
||||
globalSettings,
|
||||
connectionStrings,
|
||||
Logger,
|
||||
Constants.System.UmbracoConnectionName,
|
||||
new Lazy<IMapperCollection>(f.GetInstance<IMapperCollection>),
|
||||
TestHelper.DbProviderFactoryCreator));
|
||||
|
||||
Composition.RegisterUnique(f => f.TryGetInstance<IUmbracoDatabaseFactory>().SqlContext);
|
||||
|
||||
Composition.WithCollectionBuilder<UrlSegmentProviderCollectionBuilder>(); // empty
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
namespace Umbraco.Tests.Testing
|
||||
{
|
||||
public static class UmbracoTestOptions
|
||||
{
|
||||
public enum Logger
|
||||
{
|
||||
// pure mocks
|
||||
Mock,
|
||||
// Serilog for tests
|
||||
Serilog,
|
||||
// console logger
|
||||
Console
|
||||
}
|
||||
|
||||
public enum Database
|
||||
{
|
||||
// no database
|
||||
None,
|
||||
// new empty database file for the entire feature
|
||||
NewEmptyPerFixture,
|
||||
// new empty database file per test
|
||||
NewEmptyPerTest,
|
||||
// new database file with schema for the entire feature
|
||||
NewSchemaPerFixture,
|
||||
// new database file with schema per test
|
||||
NewSchemaPerTest
|
||||
}
|
||||
|
||||
public enum TypeLoader
|
||||
{
|
||||
// the default, global type loader for tests
|
||||
Default,
|
||||
// create one type loader for the feature
|
||||
PerFixture,
|
||||
// create one type loader for each test
|
||||
PerTest
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@
|
||||
<PackageReference Include="Microsoft.Owin.Security" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.Owin.Testing" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.Web.Infrastructure" Version="1.0.0.0" />
|
||||
<PackageReference Include="MiniProfiler" Version="4.0.138" />
|
||||
<PackageReference Include="MiniProfiler" Version="4.1.0" />
|
||||
<PackageReference Include="Moq" Version="4.13.1" />
|
||||
<PackageReference Include="NPoco" Version="4.0.2" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
@@ -148,6 +148,9 @@
|
||||
<Compile Include="Persistence\Mappers\MapperTestBase.cs" />
|
||||
<Compile Include="Persistence\Repositories\DocumentRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\EntityRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\KeyValueRepositoryTests.cs" />
|
||||
<Compile Include="Services\KeyValueServiceTests.cs" />
|
||||
<Compile Include="Persistence\Repositories\UserRepositoryTest.cs" />
|
||||
<Compile Include="UmbracoExamine\ExamineExtensions.cs" />
|
||||
<Compile Include="PropertyEditors\DataValueReferenceFactoryCollectionTests.cs" />
|
||||
<Compile Include="PublishedContent\NuCacheChildrenTests.cs" />
|
||||
@@ -231,11 +234,8 @@
|
||||
<Compile Include="Persistence\NPocoTests\NPocoSqlExtensionsTests.cs" />
|
||||
<Compile Include="Persistence\UnitOfWorkTests.cs" />
|
||||
<Compile Include="Persistence\Repositories\RedirectUrlRepositoryTests.cs" />
|
||||
<Compile Include="Testing\TestOptionAttributeBase.cs" />
|
||||
<Compile Include="Testing\UmbracoTestAttribute.cs" />
|
||||
<Compile Include="Testing\UmbracoTestBase.cs" />
|
||||
<Compile Include="TestHelpers\Entities\MockedPropertyTypes.cs" />
|
||||
<Compile Include="Testing\UmbracoTestOptions.cs" />
|
||||
<Compile Include="CoreThings\TryConvertToTests.cs" />
|
||||
<Compile Include="TestHelpers\TestObjects-Mocks.cs" />
|
||||
<Compile Include="TestHelpers\TestObjects.cs" />
|
||||
@@ -267,7 +267,6 @@
|
||||
<Compile Include="Web\ModelStateExtensionsTests.cs" />
|
||||
<Compile Include="Web\Mvc\RenderIndexActionSelectorAttributeTests.cs" />
|
||||
<Compile Include="Persistence\NPocoTests\PetaPocoCachesTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\AuditRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\DomainRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\PartialViewRepositoryTests.cs" />
|
||||
<Compile Include="Persistence\Repositories\PublicAccessRepositoryTest.cs" />
|
||||
@@ -344,7 +343,6 @@
|
||||
<Compile Include="Persistence\Repositories\TagRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\NotificationsRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\TemplateRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\UserRepositoryTest.cs" />
|
||||
<Compile Include="Models\Mapping\ContentWebModelMappingTests.cs" />
|
||||
<Compile Include="PropertyEditors\ImageCropperTest.cs" />
|
||||
<Compile Include="PublishedContent\PublishedContentExtensionTests.cs" />
|
||||
|
||||
@@ -37,6 +37,7 @@ using IUser = Umbraco.Core.Models.Membership.IUser;
|
||||
using Umbraco.Core.Mapping;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Core.Media;
|
||||
|
||||
namespace Umbraco.Tests.Web.Controllers
|
||||
{
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Smidge" Version="3.1.0" />
|
||||
<PackageReference Include="Smidge.Nuglify" Version="2.0.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -20,6 +18,7 @@
|
||||
<ProjectReference Include="..\Umbraco.Configuration\Umbraco.Configuration.csproj" />
|
||||
<ProjectReference Include="..\Umbraco.Core\Umbraco.Core.csproj" />
|
||||
<ProjectReference Include="..\Umbraco.Infrastructure\Umbraco.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\Umbraco.Web.Common\Umbraco.Web.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -5,7 +5,7 @@ using Microsoft.Extensions.Hosting;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Hosting;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
namespace Umbraco.Web.Common.AspNetCore
|
||||
{
|
||||
public class AspNetCoreApplicationShutdownRegistry : IApplicationShutdownRegistry
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
namespace Umbraco.Web.Common.AspNetCore
|
||||
{
|
||||
public class AspNetCoreBackOfficeInfo : IBackOfficeInfo
|
||||
{
|
||||
@@ -10,7 +10,7 @@ namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
GetAbsoluteUrl = globalSettings.UmbracoPath;
|
||||
}
|
||||
|
||||
public string GetAbsoluteUrl { get; }
|
||||
public string GetAbsoluteUrl { get; } // TODO make absolute
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,16 @@ using Microsoft.AspNetCore.Http;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
namespace Umbraco.Web.Common.AspNetCore
|
||||
{
|
||||
public class AspNetCoreHostingEnvironment : Umbraco.Core.Hosting.IHostingEnvironment
|
||||
{
|
||||
|
||||
|
||||
|
||||
private readonly IHostingSettings _hostingSettings;
|
||||
private readonly IWebHostEnvironment _webHostEnvironment;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
|
||||
private string _localTempPath;
|
||||
|
||||
public AspNetCoreHostingEnvironment(IHostingSettings hostingSettings, IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContextAccessor)
|
||||
@@ -30,7 +30,6 @@ namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
|
||||
ApplicationVirtualPath = "/"; //TODO how to find this, This is a server thing, not application thing.
|
||||
IISVersion = new Version(0, 0); // TODO not necessary IIS
|
||||
IsDebugMode = _hostingSettings.DebugMode;
|
||||
}
|
||||
|
||||
public bool IsHosted { get; } = true;
|
||||
@@ -39,7 +38,7 @@ namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
public string ApplicationPhysicalPath { get; }
|
||||
|
||||
public string ApplicationVirtualPath { get; }
|
||||
public bool IsDebugMode { get; }
|
||||
public bool IsDebugMode => _hostingSettings.DebugMode;
|
||||
|
||||
public Version IISVersion { get; }
|
||||
public string LocalTempPath
|
||||
@@ -103,7 +102,7 @@ namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
return applicationPath.Add(segment).Value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Umbraco.Net;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
namespace Umbraco.Web.Common.AspNetCore
|
||||
{
|
||||
public class AspNetIpResolver : IIpResolver
|
||||
{
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Umbraco.Core.Diagnostics;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
namespace Umbraco.Web.Common.AspNetCore
|
||||
{
|
||||
|
||||
public class AspNetCoreMarchal : IMarchal
|
||||
@@ -1,7 +1,7 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using IPasswordHasher = Umbraco.Core.Security.IPasswordHasher;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
namespace Umbraco.Web.Common.AspNetCore
|
||||
{
|
||||
public class AspNetCorePasswordHasher : IPasswordHasher
|
||||
{
|
||||
@@ -2,7 +2,7 @@
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Umbraco.Net;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
namespace Umbraco.Web.Common.AspNetCore
|
||||
{
|
||||
internal class AspNetCoreSessionIdResolver : ISessionIdResolver
|
||||
{
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public string SessionId
|
||||
{
|
||||
get
|
||||
@@ -1,11 +1,12 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Umbraco.Net;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
namespace Umbraco.Web.Common.AspNetCore
|
||||
{
|
||||
public class AspNetCoreUmbracoApplicationLifetime : IUmbracoApplicationLifetime
|
||||
public class AspNetCoreUmbracoApplicationLifetime : IUmbracoApplicationLifetime, IUmbracoApplicationLifetimeManager
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IHostApplicationLifetime _hostApplicationLifetime;
|
||||
@@ -32,5 +33,11 @@ namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
Thread.CurrentPrincipal = null;
|
||||
_hostApplicationLifetime.StopApplication();
|
||||
}
|
||||
|
||||
public void InvokeApplicationInit()
|
||||
{
|
||||
ApplicationInit?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
public event EventHandler ApplicationInit;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
using System;
|
||||
using System.Data.Common;
|
||||
using System.Reflection;
|
||||
@@ -7,10 +5,8 @@ using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Smidge;
|
||||
using Smidge.Nuglify;
|
||||
using Smidge.Options;
|
||||
using Umbraco.Composing;
|
||||
using Umbraco.Configuration;
|
||||
using Umbraco.Core;
|
||||
@@ -22,10 +18,11 @@ using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Logging.Serilog;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Runtime;
|
||||
using Umbraco.Web.Common.AspNetCore;
|
||||
using Umbraco.Web.Common.Runtime.Profiler;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
namespace Umbraco.Web.Common.Extensions
|
||||
{
|
||||
// TODO: Move to Umbraco.Web.Common
|
||||
public static class UmbracoCoreServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -77,7 +74,8 @@ namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
public static IServiceCollection AddUmbracoCore(this IServiceCollection services, IWebHostEnvironment webHostEnvironment, IRegister umbContainer, Assembly entryAssembly)
|
||||
{
|
||||
if (services is null) throw new ArgumentNullException(nameof(services));
|
||||
if (umbContainer is null) throw new ArgumentNullException(nameof(umbContainer));
|
||||
var container = umbContainer;
|
||||
if (container is null) throw new ArgumentNullException(nameof(container));
|
||||
if (entryAssembly is null) throw new ArgumentNullException(nameof(entryAssembly));
|
||||
|
||||
// Special case! The generic host adds a few default services but we need to manually add this one here NOW because
|
||||
@@ -103,7 +101,7 @@ namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
backOfficeInfo,
|
||||
typeFinder);
|
||||
|
||||
var factory = coreRuntime.Configure(umbContainer);
|
||||
var factory = coreRuntime.Configure(container);
|
||||
|
||||
return services;
|
||||
}
|
||||
@@ -159,7 +157,9 @@ namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
new AspNetCoreMarchal());
|
||||
|
||||
backOfficeInfo = new AspNetCoreBackOfficeInfo(globalSettings);
|
||||
profiler = new LogProfiler(logger);
|
||||
profiler = GetWebProfiler(hostingEnvironment, httpContextAccessor);
|
||||
|
||||
Current.Initialize(logger, configs, ioHelper, hostingEnvironment, backOfficeInfo, profiler);
|
||||
|
||||
return services;
|
||||
}
|
||||
@@ -173,6 +173,21 @@ namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
return services;
|
||||
}
|
||||
|
||||
private static IProfiler GetWebProfiler(Umbraco.Core.Hosting.IHostingEnvironment hostingEnvironment, IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
// create and start asap to profile boot
|
||||
if (!hostingEnvironment.IsDebugMode)
|
||||
{
|
||||
// should let it be null, that's how MiniProfiler is meant to work,
|
||||
// but our own IProfiler expects an instance so let's get one
|
||||
return new VoidProfiler();
|
||||
}
|
||||
|
||||
var webProfiler = new WebProfiler(httpContextAccessor);
|
||||
webProfiler.StartBoot();
|
||||
|
||||
return webProfiler;
|
||||
}
|
||||
private class AspNetCoreBootPermissionsChecker : IUmbracoBootPermissionChecker
|
||||
{
|
||||
public void ThrowIfNotPermissions()
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using StackExchange.Profiling;
|
||||
using Umbraco.Web.Common.Middleware;
|
||||
|
||||
namespace Umbraco.Web.Common.Extensions
|
||||
{
|
||||
public static class UmbracoRequestApplicationBuilderExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseUmbracoRequest(this IApplicationBuilder app)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
|
||||
|
||||
app.UseMiddleware<UmbracoRequestMiddleware>();
|
||||
app.UseMiddleware<MiniProfilerMiddleware>();
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetime.cs
Normal file
11
src/Umbraco.Web.Common/Lifetime/IUmbracoRequestLifetime.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Umbraco.Web.Common.Lifetime
|
||||
{
|
||||
public interface IUmbracoRequestLifetime
|
||||
{
|
||||
event EventHandler<HttpContext> RequestStart;
|
||||
event EventHandler<HttpContext> RequestEnd;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Umbraco.Web.Common.Lifetime
|
||||
{
|
||||
public interface IUmbracoRequestLifetimeManager
|
||||
{
|
||||
void InitRequest(HttpContext context);
|
||||
void EndRequest(HttpContext context);
|
||||
}
|
||||
}
|
||||
21
src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs
Normal file
21
src/Umbraco.Web.Common/Lifetime/UmbracoRequestLifetime.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Umbraco.Web.Common.Lifetime
|
||||
{
|
||||
public class UmbracoRequestLifetime : IUmbracoRequestLifetime, IUmbracoRequestLifetimeManager
|
||||
{
|
||||
public event EventHandler<HttpContext> RequestStart;
|
||||
public event EventHandler<HttpContext> RequestEnd;
|
||||
|
||||
public void InitRequest(HttpContext context)
|
||||
{
|
||||
RequestStart?.Invoke(this, context);
|
||||
}
|
||||
|
||||
public void EndRequest(HttpContext context)
|
||||
{
|
||||
RequestEnd?.Invoke(this, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Umbraco.Web.Common.Lifetime;
|
||||
|
||||
namespace Umbraco.Web.Common.Middleware
|
||||
{
|
||||
public class UmbracoRequestMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IUmbracoRequestLifetimeManager _umbracoRequestLifetimeManager;
|
||||
public UmbracoRequestMiddleware(RequestDelegate next, IUmbracoRequestLifetimeManager umbracoRequestLifetimeManager)
|
||||
{
|
||||
_next = next;
|
||||
_umbracoRequestLifetimeManager = umbracoRequestLifetimeManager;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
_umbracoRequestLifetimeManager.InitRequest(context);
|
||||
await _next(context);
|
||||
_umbracoRequestLifetimeManager.EndRequest(context);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
29
src/Umbraco.Web.Common/Runtime/AspNetCoreComponent.cs
Normal file
29
src/Umbraco.Web.Common/Runtime/AspNetCoreComponent.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Net;
|
||||
|
||||
namespace Umbraco.Web.Common.Runtime
|
||||
{
|
||||
public sealed class AspNetCoreComponent : IComponent
|
||||
{
|
||||
private readonly IHostApplicationLifetime _hostApplicationLifetime;
|
||||
private readonly IUmbracoApplicationLifetimeManager _umbracoApplicationLifetimeManager;
|
||||
|
||||
public AspNetCoreComponent(IHostApplicationLifetime hostApplicationLifetime, IUmbracoApplicationLifetimeManager umbracoApplicationLifetimeManager)
|
||||
{
|
||||
_hostApplicationLifetime = hostApplicationLifetime;
|
||||
_umbracoApplicationLifetimeManager = umbracoApplicationLifetimeManager;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_hostApplicationLifetime.ApplicationStarted.Register(() => {
|
||||
_umbracoApplicationLifetimeManager.InvokeApplicationInit();
|
||||
});
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,34 +2,47 @@ using Microsoft.AspNetCore.Http;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Media;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Net;
|
||||
using Umbraco.Core.Runtime;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Infrastructure.Media;
|
||||
using Umbraco.Web.Common.AspNetCore;
|
||||
using Umbraco.Web.Common.Lifetime;
|
||||
using Umbraco.Web.Models.PublishedContent;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.Templates;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
namespace Umbraco.Web.Common.Runtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds/replaces AspNetCore specific services
|
||||
/// </summary>
|
||||
[ComposeBefore(typeof(ICoreComposer))]
|
||||
[ComposeAfter(typeof(CoreInitialComposer))]
|
||||
public class AspNetCoreComposer : IComposer
|
||||
public class AspNetCoreComposer : ComponentComposer<AspNetCoreComponent>, IComposer
|
||||
{
|
||||
public void Compose(Composition composition)
|
||||
public new void Compose(Composition composition)
|
||||
{
|
||||
base.Compose(composition);
|
||||
|
||||
// AspNetCore specific services
|
||||
composition.RegisterUnique<IHttpContextAccessor, HttpContextAccessor>();
|
||||
|
||||
// Our own netcore implementations
|
||||
composition.RegisterUnique<IUmbracoApplicationLifetime, AspNetCoreUmbracoApplicationLifetime>();
|
||||
composition.RegisterUnique<AspNetCoreUmbracoApplicationLifetime>();
|
||||
composition.RegisterUnique<IUmbracoApplicationLifetimeManager>(factory => factory.GetInstance<AspNetCoreUmbracoApplicationLifetime>());
|
||||
composition.RegisterUnique<IUmbracoApplicationLifetime>(factory => factory.GetInstance<AspNetCoreUmbracoApplicationLifetime>());
|
||||
|
||||
composition.RegisterUnique<IApplicationShutdownRegistry, AspNetCoreApplicationShutdownRegistry>();
|
||||
|
||||
// The umbraco request lifetime
|
||||
composition.RegisterUnique<UmbracoRequestLifetime>();
|
||||
composition.RegisterUnique<IUmbracoRequestLifetimeManager>(factory => factory.GetInstance<UmbracoRequestLifetime>());
|
||||
composition.RegisterUnique<IUmbracoRequestLifetime>(factory => factory.GetInstance<UmbracoRequestLifetime>());
|
||||
|
||||
composition.RegisterUnique<IPasswordHasher, AspNetCorePasswordHasher>();
|
||||
|
||||
|
||||
@@ -40,7 +53,7 @@ namespace Umbraco.Web.BackOffice.AspNetCore
|
||||
composition.RegisterUnique<IUmbracoContextAccessor, HybridUmbracoContextAccessor>();
|
||||
|
||||
// register the umbraco context factory
|
||||
// composition.RegisterUnique<IUmbracoContextFactory, UmbracoContextFactory>();
|
||||
// composition.RegisterUnique<IUmbracoContextFactory, UmbracoContextFactory>();
|
||||
composition.RegisterUnique<IPublishedUrlProvider, UrlProvider>();
|
||||
|
||||
composition.RegisterUnique<HtmlLocalLinkParser>();
|
||||
123
src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs
Normal file
123
src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using StackExchange.Profiling;
|
||||
using StackExchange.Profiling.Internal;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Web.Common.Runtime.Profiler
|
||||
{
|
||||
public class WebProfiler : IProfiler
|
||||
{
|
||||
private MiniProfiler _startupProfiler;
|
||||
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private int _first;
|
||||
|
||||
public WebProfiler(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
// create our own provider, which can provide a profiler even during boot
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Normally we would call MiniProfiler.Current.RenderIncludes(...), but because the requeststate is not set, this method does not work.
|
||||
/// We fake the requestIds from the RequestState here.
|
||||
/// </remarks>
|
||||
public string Render()
|
||||
{
|
||||
|
||||
var profiler = MiniProfiler.Current;
|
||||
if (profiler == null) return string.Empty;
|
||||
|
||||
var context = _httpContextAccessor.HttpContext;
|
||||
|
||||
var path = (profiler.Options as MiniProfilerOptions)?.RouteBasePath.Value.EnsureTrailingSlash();
|
||||
|
||||
var result = StackExchange.Profiling.Internal.Render.Includes(
|
||||
profiler,
|
||||
path: context.Request.PathBase + path,
|
||||
isAuthorized: true,
|
||||
requestIDs: new List<Guid>{ profiler.Id },
|
||||
position: RenderPosition.Right,
|
||||
showTrivial: profiler.Options.PopupShowTrivial,
|
||||
showTimeWithChildren: profiler.Options.PopupShowTimeWithChildren,
|
||||
maxTracesToShow: profiler.Options.PopupMaxTracesToShow,
|
||||
showControls:profiler.Options.ShowControls,
|
||||
startHidden: profiler.Options.PopupStartHidden);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public IDisposable Step(string name)
|
||||
{
|
||||
return MiniProfiler.Current?.Step(name);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
MiniProfiler.StartNew();
|
||||
}
|
||||
|
||||
public void StartBoot()
|
||||
{
|
||||
_startupProfiler = MiniProfiler.StartNew("Startup Profiler");
|
||||
}
|
||||
|
||||
public void StopBoot()
|
||||
{
|
||||
_startupProfiler.Stop();
|
||||
}
|
||||
|
||||
public void Stop(bool discardResults = false)
|
||||
{
|
||||
MiniProfiler.Current?.Stop(discardResults);
|
||||
}
|
||||
|
||||
|
||||
public void UmbracoApplicationBeginRequest(HttpContext context)
|
||||
{
|
||||
if (ShouldProfile(context.Request))
|
||||
{
|
||||
Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void UmbracoApplicationEndRequest(HttpContext context)
|
||||
{
|
||||
if (ShouldProfile(context.Request))
|
||||
{
|
||||
Stop();
|
||||
|
||||
// if this is the first request, append the startup profiler
|
||||
var first = Interlocked.Exchange(ref _first, 1) == 0;
|
||||
if (first)
|
||||
{
|
||||
|
||||
var startupDuration = _startupProfiler.Root.DurationMilliseconds.GetValueOrDefault();
|
||||
MiniProfiler.Current.DurationMilliseconds += startupDuration;
|
||||
MiniProfiler.Current.GetTimingHierarchy().First().DurationMilliseconds += startupDuration;
|
||||
MiniProfiler.Current.Root.AddChild(_startupProfiler.Root);
|
||||
|
||||
_startupProfiler = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ShouldProfile(HttpRequest request)
|
||||
{
|
||||
if (new Uri(request.GetEncodedUrl(), UriKind.RelativeOrAbsolute).IsClientSideRequest()) return false;
|
||||
if (bool.TryParse(request.Query["umbDebug"], out var umbDebug)) return umbDebug;
|
||||
if (bool.TryParse(request.Headers["X-UMB-DEBUG"], out var xUmbDebug)) return xUmbDebug;
|
||||
if (bool.TryParse(request.Cookies["UMB-DEBUG"], out var cUmbDebug)) return cUmbDebug;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Net;
|
||||
using Umbraco.Web.Common.Lifetime;
|
||||
using Umbraco.Web.Common.Middleware;
|
||||
|
||||
namespace Umbraco.Web.Common.Runtime.Profiler
|
||||
{
|
||||
internal sealed class WebProfilerComponent : IComponent
|
||||
{
|
||||
private readonly bool _profile;
|
||||
private readonly WebProfiler _profiler;
|
||||
private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime;
|
||||
private readonly IUmbracoRequestLifetime _umbracoRequestLifetime;
|
||||
|
||||
public WebProfilerComponent(IProfiler profiler, ILogger logger, IUmbracoRequestLifetime umbracoRequestLifetime,
|
||||
IUmbracoApplicationLifetime umbracoApplicationLifetime)
|
||||
{
|
||||
_umbracoRequestLifetime = umbracoRequestLifetime;
|
||||
_umbracoApplicationLifetime = umbracoApplicationLifetime;
|
||||
_profile = true;
|
||||
|
||||
// although registered in WebRuntime.Compose, ensure that we have not
|
||||
// been replaced by another component, and we are still "the" profiler
|
||||
_profiler = profiler as WebProfiler;
|
||||
if (_profiler != null) return;
|
||||
|
||||
// if VoidProfiler was registered, let it be known
|
||||
if (profiler is VoidProfiler)
|
||||
logger.Info<WebProfilerComponent>(
|
||||
"Profiler is VoidProfiler, not profiling (must run debug mode to profile).");
|
||||
_profile = false;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if (!_profile) return;
|
||||
|
||||
// bind to ApplicationInit - ie execute the application initialization for *each* application
|
||||
// it would be a mistake to try and bind to the current application events
|
||||
_umbracoApplicationLifetime.ApplicationInit += InitializeApplication;
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
}
|
||||
|
||||
private void InitializeApplication(object sender, EventArgs args)
|
||||
{
|
||||
_umbracoRequestLifetime.RequestStart +=
|
||||
(sender, context) => _profiler.UmbracoApplicationBeginRequest(context);
|
||||
_umbracoRequestLifetime.RequestEnd += (sender, context) => _profiler.UmbracoApplicationEndRequest(context);
|
||||
|
||||
// Stop the profiling of the booting process
|
||||
_profiler.StopBoot();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Web.Common.Runtime.Profiler
|
||||
{
|
||||
internal class WebProfilerComposer : ComponentComposer<WebProfilerComponent>, ICoreComposer
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,13 @@
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Runtime;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Smidge
|
||||
namespace Umbraco.Web.Common.RuntimeMinification
|
||||
{
|
||||
public sealed class SmidgeComposer : IComposer
|
||||
{
|
||||
public void Compose(Composition composition)
|
||||
{
|
||||
composition.Register<IRuntimeMinifier, SmidgeRuntimeMinifier>(Lifetime.Scope);
|
||||
composition.Register<IRuntimeMinifier, SmidgeRuntimeMinifier>(Core.Composing.Lifetime.Scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ using Umbraco.Web.JavaScript;
|
||||
using CssFile = Smidge.Models.CssFile;
|
||||
using JavaScriptFile = Smidge.Models.JavaScriptFile;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Smidge
|
||||
namespace Umbraco.Web.Common.RuntimeMinification
|
||||
{
|
||||
public class SmidgeRuntimeMinifier : IRuntimeMinifier
|
||||
{
|
||||
25
src/Umbraco.Web.Common/Umbraco.Web.Common.csproj
Normal file
25
src/Umbraco.Web.Common/Umbraco.Web.Common.csproj
Normal file
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<OutputType>Library</OutputType>
|
||||
<LangVersion>8</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Umbraco.Configuration\Umbraco.Configuration.csproj" />
|
||||
<ProjectReference Include="..\Umbraco.Core\Umbraco.Core.csproj" />
|
||||
<ProjectReference Include="..\Umbraco.Infrastructure\Umbraco.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.1.0" />
|
||||
<PackageReference Include="Smidge" Version="3.1.0" />
|
||||
<PackageReference Include="Smidge.Nuglify" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -3,7 +3,7 @@
|
||||
* @name umbraco.services.mediaHelper
|
||||
* @description A helper object used for dealing with media items
|
||||
**/
|
||||
function mediaHelper(umbRequestHelper, $log) {
|
||||
function mediaHelper(umbRequestHelper, $http, $log) {
|
||||
|
||||
//container of fileresolvers
|
||||
var _mediaFileResolvers = {};
|
||||
@@ -304,11 +304,6 @@ function mediaHelper(umbRequestHelper, $log) {
|
||||
return imagePath;
|
||||
}
|
||||
|
||||
// Check if file is a svg
|
||||
if (this.getFileExtension(imagePath) === "svg") {
|
||||
return imagePath;
|
||||
}
|
||||
|
||||
// If the path is not an image we cannot get a thumb
|
||||
if (!this.detectIfImageByExtension(imagePath)) {
|
||||
return null;
|
||||
@@ -399,6 +394,54 @@ function mediaHelper(umbRequestHelper, $log) {
|
||||
var lowered = filePath.toLowerCase();
|
||||
var ext = lowered.substr(lowered.lastIndexOf(".") + 1);
|
||||
return ext;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.mediaHelper#getProcessedImageUrl
|
||||
* @methodOf umbraco.services.mediaHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns image URL with configured crop and other processing parameters.
|
||||
*
|
||||
* @param {string} imagePath Raw image path
|
||||
* @param {object} options Object describing image generation parameters:
|
||||
* {
|
||||
* animationProcessMode: <string>
|
||||
* cacheBusterValue: <string>
|
||||
* focalPoint: {
|
||||
* left: <int>
|
||||
* top: <int>
|
||||
* },
|
||||
* height: <int>
|
||||
* mode: <string>
|
||||
* upscale: <boolean>
|
||||
* width: <int>
|
||||
* }
|
||||
*/
|
||||
getProcessedImageUrl: function (imagePath, options) {
|
||||
|
||||
if (!options) {
|
||||
return imagePath;
|
||||
}
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"imagesApiBaseUrl",
|
||||
"GetProcessedImageUrl",
|
||||
{
|
||||
imagePath,
|
||||
animationProcessMode: options.animationProcessMode,
|
||||
cacheBusterValue: options.cacheBusterValue,
|
||||
focalPoint: options.focalPoint,
|
||||
height: options.height,
|
||||
mode: options.mode,
|
||||
upscale: options.upscale || false,
|
||||
width: options.width
|
||||
})),
|
||||
"Failed to retrieve processed image URL for image: " + imagePath);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* A service containing all logic for all of the Umbraco TinyMCE plugins
|
||||
*/
|
||||
function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, stylesheetResource, macroResource, macroService,
|
||||
$routeParams, umbRequestHelper, angularHelper, userService, editorService, entityResource, eventsService, localStorageService) {
|
||||
$routeParams, umbRequestHelper, angularHelper, userService, editorService, entityResource, eventsService, localStorageService, mediaHelper) {
|
||||
|
||||
//These are absolutely required in order for the macros to render inline
|
||||
//we put these as extended elements because they get merged on top of the normal allowed elements by tiny mce
|
||||
@@ -298,15 +298,20 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
if (editor.settings.maxImageSize && editor.settings.maxImageSize !== 0) {
|
||||
var newSize = imageHelper.scaleToMaxSize(editor.settings.maxImageSize, size.w, size.h);
|
||||
|
||||
|
||||
editor.dom.setAttrib(imageDomElement, 'width', newSize.width);
|
||||
editor.dom.setAttrib(imageDomElement, 'height', newSize.height);
|
||||
|
||||
// Images inserted via Media Picker will have a URL we can use for ImageResizer QueryStrings
|
||||
// Images pasted/dragged in are not persisted to media until saved & thus will need to be added
|
||||
if(imgUrl){
|
||||
var src = imgUrl + "?width=" + newSize.width + "&height=" + newSize.height;
|
||||
editor.dom.setAttrib(imageDomElement, 'data-mce-src', src);
|
||||
if (imgUrl) {
|
||||
mediaHelper.getProcessedImageUrl(imgUrl,
|
||||
{
|
||||
height: newSize.height,
|
||||
width: newSize.width
|
||||
})
|
||||
.then(function (resizedImgUrl) {
|
||||
editor.dom.setAttrib(imageDomElement, 'data-mce-src', resizedImgUrl);
|
||||
});
|
||||
}
|
||||
|
||||
editor.execCommand("mceAutoResize", false, null, null);
|
||||
@@ -1495,10 +1500,17 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
});
|
||||
|
||||
args.editor.on('ObjectResized', function (e) {
|
||||
var qs = "?width=" + e.width + "&height=" + e.height + "&mode=max";
|
||||
var srcAttr = $(e.target).attr("src");
|
||||
var path = srcAttr.split("?")[0];
|
||||
$(e.target).attr("data-mce-src", path + qs);
|
||||
mediaHelper.getProcessedImageUrl(path,
|
||||
{
|
||||
height: e.height,
|
||||
moded: "max",
|
||||
width: e.width
|
||||
})
|
||||
.then(function (resizedPath) {
|
||||
$(e.target).attr("data-mce-src", resizedPath);
|
||||
});
|
||||
|
||||
syncContent();
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user