diff --git a/src/Umbraco.Core/Deploy/ArtifactDependency.cs b/src/Umbraco.Core/Deploy/ArtifactDependency.cs
index 80a77740d0..aeb2cfe4a6 100644
--- a/src/Umbraco.Core/Deploy/ArtifactDependency.cs
+++ b/src/Umbraco.Core/Deploy/ArtifactDependency.cs
@@ -34,7 +34,7 @@ public class ArtifactDependency
///
/// true if the dependency is included when building a dependency tree and gets deployed in the correct order; otherwise, false.
///
- public bool Ordering { get; }
+ public bool Ordering { get; internal set; }
///
/// Gets the dependency mode.
@@ -42,7 +42,7 @@ public class ArtifactDependency
///
/// The dependency mode.
///
- public ArtifactDependencyMode Mode { get; }
+ public ArtifactDependencyMode Mode { get; internal set; }
///
/// Gets or sets the checksum.
diff --git a/src/Umbraco.Core/Deploy/ArtifactDependencyCollection.cs b/src/Umbraco.Core/Deploy/ArtifactDependencyCollection.cs
index 7f2b05eaad..6446af1ad5 100644
--- a/src/Umbraco.Core/Deploy/ArtifactDependencyCollection.cs
+++ b/src/Umbraco.Core/Deploy/ArtifactDependencyCollection.cs
@@ -21,25 +21,39 @@ public class ArtifactDependencyCollection : ICollection
///
public void Add(ArtifactDependency item)
{
- if (item.Mode == ArtifactDependencyMode.Exist &&
- _dependencies.TryGetValue(item.Udi, out ArtifactDependency? existingItem) &&
- existingItem.Mode == ArtifactDependencyMode.Match)
+ if (_dependencies.TryGetValue(item.Udi, out ArtifactDependency? existingItem))
{
- // Don't downgrade dependency mode from Match to Exist
- return;
- }
+ // Update existing item
+ if (existingItem.Mode is ArtifactDependencyMode.Exist)
+ {
+ // Allow updating dependency mode from Exist to Match
+ existingItem.Mode = item.Mode;
+ }
- _dependencies[item.Udi] = item;
+ if (existingItem.Ordering is false)
+ {
+ // Allow updating non-ordering to ordering
+ existingItem.Ordering = item.Ordering;
+ }
+
+ if (string.IsNullOrEmpty(item.Checksum) is false)
+ {
+ // Allow updating checksum if set
+ existingItem.Checksum = item.Checksum;
+ }
+ }
+ else
+ {
+ // Add new item
+ _dependencies[item.Udi] = item;
+ }
}
///
public void Clear() => _dependencies.Clear();
///
- public bool Contains(ArtifactDependency item)
- => _dependencies.TryGetValue(item.Udi, out ArtifactDependency? existingItem) &&
- // Check whether it has the same or higher dependency mode
- (existingItem.Mode == item.Mode || existingItem.Mode == ArtifactDependencyMode.Match);
+ public bool Contains(ArtifactDependency item) => _dependencies.ContainsKey(item.Udi);
///
public void CopyTo(ArtifactDependency[] array, int arrayIndex) => _dependencies.Values.CopyTo(array, arrayIndex);
diff --git a/src/Umbraco.Infrastructure/Migrations/PreMigration/DataTypeSplitDataCollector.cs b/src/Umbraco.Infrastructure/Migrations/PreMigration/DataTypeSplitDataCollector.cs
new file mode 100644
index 0000000000..3a133bc253
--- /dev/null
+++ b/src/Umbraco.Infrastructure/Migrations/PreMigration/DataTypeSplitDataCollector.cs
@@ -0,0 +1,169 @@
+using Microsoft.Extensions.Logging;
+using Umbraco.Cms.Core;
+using Umbraco.Cms.Core.Composing;
+using Umbraco.Cms.Core.Configuration;
+using Umbraco.Cms.Core.DependencyInjection;
+using Umbraco.Cms.Core.Events;
+using Umbraco.Cms.Core.Manifest;
+using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Notifications;
+using Umbraco.Cms.Core.PropertyEditors;
+using Umbraco.Cms.Core.Scoping;
+using Umbraco.Cms.Core.Serialization;
+using Umbraco.Cms.Core.Services;
+using Umbraco.Cms.Core.Sync;
+
+namespace Umbraco.Cms.Infrastructure.Migrations.PreMigration;
+
+// we use a composer here so its easier to clean this up when we no longer need it.
+// same for additional classes in the same file, nice and self contained
+// should only be used to migrate from v13 to v14
+// ⚠️ FIXME: PLEASE DELETE THIS IN V14! ⚠️
+public class DataTypeSplitDataCollectorComposer : IComposer
+{
+ public void Compose(IUmbracoBuilder builder)
+ {
+ builder.AddNotificationHandler();
+ }
+}
+
+public class DataTypeSplitDataCollector : INotificationHandler
+{
+ private readonly DataEditorCollection _dataEditors;
+ private readonly IManifestParser _manifestParser;
+ private readonly IDataTypeService _dataTypeService;
+ private readonly IKeyValueService _keyValueService;
+ private readonly IJsonSerializer _jsonSerializer;
+ private readonly ILogger _logger;
+ private readonly ICoreScopeProvider _coreScopeProvider;
+ private readonly IRuntimeState _runtimeState;
+ private readonly IServerRoleAccessor _serverRoleAccessor;
+ private readonly IUmbracoVersion _umbracoVersion;
+
+ public DataTypeSplitDataCollector(
+ DataEditorCollection dataEditors,
+ IManifestParser manifestParser,
+ IDataTypeService dataTypeService,
+ IKeyValueService keyValueService,
+ IJsonSerializer jsonSerializer,
+ ILogger logger,
+ ICoreScopeProvider coreScopeProvider,
+ IRuntimeState runtimeState,
+ IServerRoleAccessor serverRoleAccessor,
+ IUmbracoVersion umbracoVersion)
+ {
+ _dataEditors = dataEditors;
+ _manifestParser = manifestParser;
+ _dataTypeService = dataTypeService;
+ _keyValueService = keyValueService;
+ _jsonSerializer = jsonSerializer;
+ _logger = logger;
+ _coreScopeProvider = coreScopeProvider;
+ _runtimeState = runtimeState;
+ _serverRoleAccessor = serverRoleAccessor;
+ _umbracoVersion = umbracoVersion;
+ }
+
+ public void Handle(UmbracoApplicationStartedNotification notification)
+ {
+ // should only be used to collect data in v13
+ if (_umbracoVersion.Version.Major is not 13)
+ {
+ return;
+ }
+
+ // only run this if the application is actually running and not in an install/upgrade state
+ if (_runtimeState.Level != RuntimeLevel.Run)
+ {
+ return;
+ }
+
+ // do not run on load balanced subscribers
+ if (_serverRoleAccessor.CurrentServerRole == ServerRole.Subscriber)
+ {
+ return;
+ }
+
+ var manifestEditorsData = _manifestParser.CombinedManifest.PropertyEditors
+ .Select(pe => new EditorAliasSplitData(pe.Alias){EditorUiAlias = pe.Alias, EditorAlias = EditorAliasFromValueEditorValueType(pe.GetValueEditor().ValueType)})
+ .ToDictionary(data => data.OriginalEditorAlias);
+
+ _logger.LogDebug("Found {count} custom PropertyEditor(s) configured trough manifest files",manifestEditorsData.Count);
+
+ var fromCodeEditorsData = _dataEditors
+ .Where(de =>
+ de.GetType().Assembly.GetName().FullName
+ .StartsWith("umbraco.core", StringComparison.InvariantCultureIgnoreCase) is false
+ && de.GetType().Assembly.GetName().FullName
+ .StartsWith("umbraco.infrastructure", StringComparison.InvariantCultureIgnoreCase) is false)
+ .Select(de => new EditorAliasSplitData(de.Alias) { EditorAlias = de.Alias })
+ .ToDictionary(data => data.OriginalEditorAlias);
+
+ _logger.LogDebug("Found {count} custom PropertyEditor(s) configured trough code",fromCodeEditorsData.Count);
+
+ var combinedEditorsData = new Dictionary(manifestEditorsData);
+ foreach (KeyValuePair pair in fromCodeEditorsData)
+ {
+ combinedEditorsData.Add(pair.Key,pair.Value);
+ }
+
+ if (combinedEditorsData.Any() == false)
+ {
+ _logger.LogDebug("No custom PropertyEditors found, skipping collection datatype migration data.");
+ return;
+ }
+
+ using ICoreScope coreScope = _coreScopeProvider.CreateCoreScope(autoComplete: true);
+
+ IEnumerable dataTypes = _dataTypeService.GetAll();
+
+ DataTypeEditorAliasMigrationData[] migrationData = dataTypes
+ .Where(dt => combinedEditorsData.ContainsKey(dt.EditorAlias))
+ .Select(dt => new DataTypeEditorAliasMigrationData
+ {
+ DataTypeId = dt.Id,
+ EditorAlias = combinedEditorsData[dt.EditorAlias].EditorAlias,
+ EditorUiAlias = combinedEditorsData[dt.EditorAlias].EditorUiAlias
+ }).ToArray();
+
+ _logger.LogDebug("Collected migration data for {count} DataType(s) that use custom PropertyEditors",migrationData.Length);
+
+ _keyValueService.SetValue("migrateDataEditorSplitCollectionData",_jsonSerializer.Serialize(migrationData));
+ }
+
+ private class EditorAliasSplitData
+ {
+ public EditorAliasSplitData(string originalEditorAlias)
+ {
+ OriginalEditorAlias = originalEditorAlias;
+ }
+
+ public string OriginalEditorAlias { get; init; }
+ public string? EditorUiAlias { get; init; }
+ public string? EditorAlias { get; init; }
+ }
+
+ private class DataTypeEditorAliasMigrationData
+ {
+ public int DataTypeId { get; set; }
+ public string? EditorUiAlias { get; init; }
+ public string? EditorAlias { get; init; }
+ }
+
+ private string EditorAliasFromValueEditorValueType(string valueType)
+ {
+ switch (valueType)
+ {
+ case ValueTypes.Date: return "Umbraco.Plain.DateTime";
+ case ValueTypes.DateTime: return "Umbraco.Plain.DateTime";
+ case ValueTypes.Decimal: return "Umbraco.Plain.Decimal";
+ case ValueTypes.Integer: return "Umbraco.Plain.Integer";
+ case ValueTypes.Bigint: return "Umbraco.Plain.Integer";
+ case ValueTypes.Json: return "Umbraco.Plain.Json";
+ case ValueTypes.Time: return "Umbraco.Plain.Time";
+ case ValueTypes.String: return "Umbraco.Plain.String";
+ case ValueTypes.Xml: return "Umbraco.Plain.String";
+ default:return string.Empty;
+ }
+ }
+}
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
index 84eda76fa5..f1b205b3aa 100644
--- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
@@ -1229,8 +1229,11 @@ AND umbracoNode.id <> @id",
/// If this is not done, then in some cases the "edited" value for a particular culture for a document will remain true
/// when it should be false
/// if the property was changed to invariant. In order to do this we need to recalculate this value based on the values
- /// stored for each
- /// property, culture and current/published version.
+ /// stored for each property, culture and current/published version.
+ ///
+ /// Some of the sql statements in this function have a tendency to take a lot of parameters (nodeIds)
+ /// as the WhereIn Npoco method translates all the nodeIds being passed in as parameters when using the SqlClient provider.
+ /// this results in to many parameters (>2100) error => We need to batch the calls
///
private void RenormalizeDocumentEditedFlags(
IReadOnlyCollection propertyTypeIds,
@@ -1386,16 +1389,19 @@ AND umbracoNode.id <> @id",
// Now bulk update the table DocumentCultureVariationDto, once for edited = true, another for edited = false
foreach (IGrouping editValue in toUpdate.GroupBy(x => x.Edited))
{
- Database.Execute(Sql().Update(u => u.Set(x => x.Edited, editValue.Key))
- .WhereIn(x => x.Id, editValue.Select(x => x.Id)));
+ // update in batches to account for maximum parameter count
+ foreach (IEnumerable batchedValues in editValue.InGroupsOf(Constants.Sql.MaxParameterCount))
+ {
+ Database.Execute(Sql().Update(u => u.Set(x => x.Edited, editValue.Key))
+ .WhereIn(x => x.Id, batchedValues.Select(x => x.Id)));
+ }
}
// Now bulk update the umbracoDocument table
- // we need to do this in batches as the WhereIn Npoco method translates to all the nodeIds being passed in as parameters when using the SqlClient provider
- // this results in to many parameters (>2100) being passed to the client when there are a lot of documents being normalized
foreach (IGrouping> groupByValue in editedDocument.GroupBy(x => x.Value))
{
- foreach (IEnumerable> batch in groupByValue.InGroupsOf(2000))
+ // update in batches to account for maximum parameter count
+ foreach (IEnumerable> batch in groupByValue.InGroupsOf(Constants.Sql.MaxParameterCount))
{
Database.Execute(Sql().Update(u => u.Set(x => x.Edited, groupByValue.Key))
.WhereIn(x => x.NodeId, batch.Select(x => x.Key)));
diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
index 8d523ecd4f..c61c621398 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
@@ -778,6 +778,7 @@ public class ContentController : ContentControllerBase
/// The content id to copy
/// The name of the blueprint
///
+ [Authorize(Policy = AuthorizationPolicies.ContentPermissionCreateBlueprintFromId)]
[HttpPost]
public ActionResult CreateBlueprintFromContent(
[FromQuery] int contentId,
@@ -833,7 +834,9 @@ public class ContentController : ContentControllerBase
///
/// Saves content
///
- [ContentSaveValidation]
+ [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)]
+ [FileUploadCleanupFilter]
+ [ContentSaveValidation(skipUserAccessValidation:true)] // skip user access validation because we "only" require Settings access to create new blueprints from scratch
public async Task?>?> PostSaveBlueprint(
[ModelBinder(typeof(BlueprintItemBinder))] ContentItemSave contentItem)
{
@@ -1993,6 +1996,7 @@ public class ContentController : ContentControllerBase
return Ok();
}
+ [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)]
[HttpDelete]
[HttpPost]
public IActionResult DeleteBlueprint(int id)
diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeAuth.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeAuth.cs
index 86c2194a4c..e8151559ac 100644
--- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeAuth.cs
+++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeAuth.cs
@@ -180,6 +180,13 @@ public static partial class UmbracoBuilderExtensions
policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionDelete.ActionLetter));
});
+ options.AddPolicy(AuthorizationPolicies.ContentPermissionCreateBlueprintFromId, policy =>
+ {
+ policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme);
+ policy.Requirements.Add(
+ new ContentPermissionsQueryStringRequirement(ActionCreateBlueprintFromContent.ActionLetter, "contentId"));
+ });
+
options.AddPolicy(AuthorizationPolicies.BackOfficeAccess, policy =>
{
policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme);
diff --git a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs
index 100d089451..f7be9d129a 100644
--- a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs
+++ b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs
@@ -20,9 +20,12 @@ namespace Umbraco.Cms.Web.BackOffice.Filters;
///
internal sealed class ContentSaveValidationAttribute : TypeFilterAttribute
{
- public ContentSaveValidationAttribute() : base(typeof(ContentSaveValidationFilter)) =>
+ public ContentSaveValidationAttribute(bool skipUserAccessValidation = false)
+ : base(typeof(ContentSaveValidationFilter))
+ {
Order = -3000; // More important than ModelStateInvalidFilter.FilterOrder
-
+ Arguments = new object[] { skipUserAccessValidation };
+ }
private sealed class ContentSaveValidationFilter : IAsyncActionFilter
{
@@ -32,6 +35,7 @@ internal sealed class ContentSaveValidationAttribute : TypeFilterAttribute
private readonly ILocalizationService _localizationService;
private readonly ILoggerFactory _loggerFactory;
private readonly IPropertyValidationService _propertyValidationService;
+ private readonly bool _skipUserAccessValidation;
public ContentSaveValidationFilter(
@@ -40,7 +44,8 @@ internal sealed class ContentSaveValidationAttribute : TypeFilterAttribute
IPropertyValidationService propertyValidationService,
IAuthorizationService authorizationService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
- ILocalizationService localizationService)
+ ILocalizationService localizationService,
+ bool skipUserAccessValidation)
{
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
_contentService = contentService ?? throw new ArgumentNullException(nameof(contentService));
@@ -49,6 +54,7 @@ internal sealed class ContentSaveValidationAttribute : TypeFilterAttribute
_authorizationService = authorizationService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
_localizationService = localizationService;
+ _skipUserAccessValidation = skipUserAccessValidation;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
@@ -88,7 +94,7 @@ internal sealed class ContentSaveValidationAttribute : TypeFilterAttribute
return;
}
- if (!await ValidateUserAccessAsync(model, context))
+ if (_skipUserAccessValidation is false && await ValidateUserAccessAsync(model, context) is false)
{
return;
}
diff --git a/src/Umbraco.Web.Common/Authorization/AuthorizationPolicies.cs b/src/Umbraco.Web.Common/Authorization/AuthorizationPolicies.cs
index 0441f3a733..1b15409813 100644
--- a/src/Umbraco.Web.Common/Authorization/AuthorizationPolicies.cs
+++ b/src/Umbraco.Web.Common/Authorization/AuthorizationPolicies.cs
@@ -23,6 +23,7 @@ public static class AuthorizationPolicies
public const string ContentPermissionProtectById = nameof(ContentPermissionProtectById);
public const string ContentPermissionBrowseById = nameof(ContentPermissionBrowseById);
public const string ContentPermissionDeleteById = nameof(ContentPermissionDeleteById);
+ public const string ContentPermissionCreateBlueprintFromId = nameof(ContentPermissionCreateBlueprintFromId);
public const string MediaPermissionByResource = nameof(MediaPermissionByResource);
public const string MediaPermissionPathById = nameof(MediaPermissionPathById);
diff --git a/templates/Umbraco.Templates.csproj b/templates/Umbraco.Templates.csproj
index 8e3e327bd6..854a3095d7 100644
--- a/templates/Umbraco.Templates.csproj
+++ b/templates/Umbraco.Templates.csproj
@@ -42,6 +42,7 @@
+
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs b/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs
index 21a06c3643..0ca0b2f579 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs
@@ -58,21 +58,64 @@ public class IndexInitializer
_contentTypeService = contentTypeService;
}
+ [Obsolete("Use ctor that is not obsolete. This will be removed in Umbraco 15.")]
public IndexInitializer(
IShortStringHelper shortStringHelper,
PropertyEditorCollection propertyEditors,
MediaUrlGeneratorCollection mediaUrlGenerators,
IScopeProvider scopeProvider,
ILoggerFactory loggerFactory,
- IOptions contentSettings, IContentTypeService contentTypeService)
+ IOptions contentSettings,
+ ILocalizationService localizationService)
: this(
- shortStringHelper,
- propertyEditors,
- mediaUrlGenerators,
- scopeProvider,
- loggerFactory,
- contentSettings,
- StaticServiceProvider.Instance.GetRequiredService(), contentTypeService)
+ shortStringHelper,
+ propertyEditors,
+ mediaUrlGenerators,
+ scopeProvider,
+ loggerFactory,
+ contentSettings,
+ localizationService, StaticServiceProvider.Instance.GetRequiredService())
+ {
+
+ }
+
+ [Obsolete("Use ctor that is not obsolete. This will be removed in Umbraco 15.")]
+ public IndexInitializer(
+ IShortStringHelper shortStringHelper,
+ PropertyEditorCollection propertyEditors,
+ MediaUrlGeneratorCollection mediaUrlGenerators,
+ IScopeProvider scopeProvider,
+ ILoggerFactory loggerFactory,
+ IOptions contentSettings,
+ IContentTypeService contentTypeService)
+ : this(
+ shortStringHelper,
+ propertyEditors,
+ mediaUrlGenerators,
+ scopeProvider,
+ loggerFactory,
+ contentSettings,
+ StaticServiceProvider.Instance.GetRequiredService(), contentTypeService)
+ {
+ }
+
+ [Obsolete("Use ctor that is not obsolete. This will be removed in Umbraco 15.")]
+ public IndexInitializer(
+ IShortStringHelper shortStringHelper,
+ PropertyEditorCollection propertyEditors,
+ MediaUrlGeneratorCollection mediaUrlGenerators,
+ IScopeProvider scopeProvider,
+ ILoggerFactory loggerFactory,
+ IOptions contentSettings)
+ : this(
+ shortStringHelper,
+ propertyEditors,
+ mediaUrlGenerators,
+ scopeProvider,
+ loggerFactory,
+ contentSettings,
+ StaticServiceProvider.Instance.GetRequiredService(),
+ StaticServiceProvider.Instance.GetRequiredService())
{
}
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Deploy/ArtifactBaseTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Deploy/ArtifactBaseTests.cs
index f029c42749..82aa79a27f 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Deploy/ArtifactBaseTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Deploy/ArtifactBaseTests.cs
@@ -1,8 +1,6 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
-using System.Collections.Generic;
-using System.Linq;
using Newtonsoft.Json;
using NUnit.Framework;
using Umbraco.Cms.Core;
@@ -49,6 +47,71 @@ public class ArtifactBaseTests
string.Join(",", artifact.Dependencies.Select(x => x.Udi.ToString())));
}
+ [Test]
+ public void Dependencies_Correctly_Updates_Mode()
+ {
+ var udi = Udi.Create(Constants.UdiEntityType.AnyGuid, Guid.NewGuid());
+
+ var dependencies = new ArtifactDependencyCollection
+ {
+ // Keep Match
+ new ArtifactDependency(udi, false, ArtifactDependencyMode.Match),
+ new ArtifactDependency(udi, false, ArtifactDependencyMode.Exist),
+ };
+
+ Assert.AreEqual(1, dependencies.Count);
+ var dependency = dependencies.First();
+ Assert.AreEqual(udi, dependency.Udi);
+ Assert.AreEqual(false, dependency.Ordering);
+ Assert.AreEqual(ArtifactDependencyMode.Match, dependency.Mode);
+ Assert.AreEqual(null, dependency.Checksum);
+ }
+
+ [Test]
+ public void Dependencies_Correctly_Updates_Ordering()
+ {
+ var udi = Udi.Create(Constants.UdiEntityType.AnyGuid, Guid.NewGuid());
+
+ var dependencies = new ArtifactDependencyCollection
+ {
+ // Keep ordering (regardless of mode)
+ new ArtifactDependency(udi, false, ArtifactDependencyMode.Match),
+ new ArtifactDependency(udi, false, ArtifactDependencyMode.Exist),
+ new ArtifactDependency(udi, true, ArtifactDependencyMode.Match),
+ new ArtifactDependency(udi, true, ArtifactDependencyMode.Exist),
+ new ArtifactDependency(udi, false, ArtifactDependencyMode.Match),
+ new ArtifactDependency(udi, false, ArtifactDependencyMode.Exist),
+ };
+
+ Assert.AreEqual(1, dependencies.Count);
+ var dependency = dependencies.First();
+ Assert.AreEqual(udi, dependency.Udi);
+ Assert.AreEqual(true, dependency.Ordering);
+ Assert.AreEqual(ArtifactDependencyMode.Match, dependency.Mode);
+ Assert.AreEqual(null, dependency.Checksum);
+ }
+
+ [Test]
+ public void Dependencies_Correctly_Updates_Checksum()
+ {
+ var udi = Udi.Create(Constants.UdiEntityType.AnyGuid, Guid.NewGuid());
+
+ var dependencies = new ArtifactDependencyCollection
+ {
+ // Keep checksum
+ new ArtifactDependency(udi, true, ArtifactDependencyMode.Match, "123"),
+ new ArtifactDependency(udi, true, ArtifactDependencyMode.Match, string.Empty),
+ new ArtifactDependency(udi, true, ArtifactDependencyMode.Match),
+ };
+
+ Assert.AreEqual(1, dependencies.Count);
+ var dependency = dependencies.First();
+ Assert.AreEqual(udi, dependency.Udi);
+ Assert.AreEqual(true, dependency.Ordering);
+ Assert.AreEqual(ArtifactDependencyMode.Match, dependency.Mode);
+ Assert.AreEqual("123", dependency.Checksum);
+ }
+
private class TestArtifact : ArtifactBase
{
public TestArtifact(GuidUdi udi, IEnumerable dependencies = null)