Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 39 additions & 19 deletions src/Umbraco.Core/Deploy/ArtifactBase.cs
Original file line number Diff line number Diff line change
@@ -1,49 +1,69 @@
namespace Umbraco.Cms.Core.Deploy;

/// <summary>
/// Provides a base class to all artifacts.
/// Provides a base class for all artifacts.
/// </summary>
/// <typeparam name="TUdi">The UDI type.</typeparam>
public abstract class ArtifactBase<TUdi> : IArtifact
where TUdi : Udi
{
private IEnumerable<ArtifactDependency> _dependencies;
private readonly Lazy<string> _checksum;

/// <summary>
/// Initializes a new instance of the <see cref="ArtifactBase{TUdi}" /> class.
/// </summary>
/// <param name="udi">The UDI.</param>
/// <param name="dependencies">The dependencies.</param>
protected ArtifactBase(TUdi udi, IEnumerable<ArtifactDependency>? dependencies = null)
{
Udi = udi ?? throw new ArgumentNullException("udi");
Udi = udi ?? throw new ArgumentNullException(nameof(udi));
Name = Udi.ToString();

_dependencies = dependencies ?? Enumerable.Empty<ArtifactDependency>();
_checksum = new Lazy<string>(GetChecksum);
}

private readonly Lazy<string> _checksum;

private IEnumerable<ArtifactDependency> _dependencies;

protected abstract string GetChecksum();

/// <inheritdoc />
Udi IArtifactSignature.Udi => Udi;

/// <inheritdoc />
public TUdi Udi { get; set; }

/// <inheritdoc />
public IEnumerable<ArtifactDependency> Dependencies
{
get => _dependencies;
set => _dependencies = value.OrderBy(x => x.Udi);
}

/// <inheritdoc />
public string Checksum => _checksum.Value;

/// <inheritdoc />
public string Name { get; set; }

/// <inheritdoc />
public string Alias { get; set; } = string.Empty;

/// <summary>
/// Gets the checksum.
/// </summary>
/// <returns>
/// The checksum.
/// </returns>
protected abstract string GetChecksum();

/// <summary>
/// Prevents the <see cref="Checksum" /> property from being serialized.
/// </summary>
/// <returns>
/// Returns <c>false</c> to prevent the property from being serialized.
/// </returns>
/// <remarks>
/// Note that we can't use <see cref="NonSerializedAttribute"/> here as that works only on fields, not properties. And we want to avoid using [JsonIgnore]
/// Note that we can't use <see cref="NonSerializedAttribute" /> here as that works only on fields, not properties. And we want to avoid using [JsonIgnore]
/// as that would require an external dependency in Umbraco.Cms.Core.
/// So using this method of excluding properties from serialized data, documented here: https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm
/// </remarks>
public bool ShouldSerializeChecksum() => false;

public IEnumerable<ArtifactDependency> Dependencies
{
get => _dependencies;
set => _dependencies = value.OrderBy(x => x.Udi);
}

public string Name { get; set; }

public string Alias { get; set; } = string.Empty;
}
24 changes: 7 additions & 17 deletions src/Umbraco.Core/Deploy/ArtifactDependency.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,17 @@
using System.Text.Json.Serialization;

namespace Umbraco.Cms.Core.Deploy;

/// <summary>
/// Represents an artifact dependency.
/// </summary>
/// <remarks>
/// <para>
/// Dependencies have an order property which indicates whether it must be respected when ordering artifacts.
/// </para>
/// <para>
/// Dependencies have a mode which can be <see cref="ArtifactDependencyMode.Match" /> or <see cref="ArtifactDependencyMode.Exist" /> depending on whether the checksum should match.
/// </para>
/// </remarks>
public class ArtifactDependency
{
/// <summary>
/// Initializes a new instance of the <see cref="ArtifactDependency" /> class.
/// </summary>
/// <param name="udi">The entity identifier of the artifact dependency.</param>
/// <param name="ordering">A value indicating whether the dependency is ordering.</param>
/// <param name="mode">The dependency mode.</param>
/// <param name="checksum">The checksum.</param>
/// <param name="ordering">A value indicating whether the dependency must be included when building a dependency tree and ensure the artifact gets deployed in the correct order.</param>
/// <param name="mode">A value indicating whether the checksum must match or the artifact just needs to exist.</param>
/// <param name="checksum">The checksum of the dependency.</param>
public ArtifactDependency(Udi udi, bool ordering, ArtifactDependencyMode mode, string? checksum = null)
{
Udi = udi;
Expand All @@ -39,10 +29,10 @@ public ArtifactDependency(Udi udi, bool ordering, ArtifactDependencyMode mode, s
public Udi Udi { get; }

/// <summary>
/// Gets a value indicating whether the dependency is ordering.
/// Gets a value indicating whether the dependency is included when building a dependency tree and gets deployed in the correct order.
/// </summary>
/// <value>
/// <c>true</c> if the dependency is ordering; otherwise, <c>false</c>.
/// <c>true</c> if the dependency is included when building a dependency tree and gets deployed in the correct order; otherwise, <c>false</c>.
/// </value>
public bool Ordering { get; }

Expand All @@ -55,10 +45,10 @@ public ArtifactDependency(Udi udi, bool ordering, ArtifactDependencyMode mode, s
public ArtifactDependencyMode Mode { get; }

/// <summary>
/// Gets the checksum.
/// Gets or sets the checksum.
/// </summary>
/// <value>
/// The checksum.
/// </value>
public string? Checksum { get; }
public string? Checksum { get; set; }
}
43 changes: 27 additions & 16 deletions src/Umbraco.Core/Deploy/ArtifactDependencyCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,53 @@
namespace Umbraco.Cms.Core.Deploy;

/// <summary>
/// Represents a collection of distinct <see cref="ArtifactDependency" />.
/// Represents a collection of distinct <see cref="ArtifactDependency" />.
/// </summary>
/// <remarks>The collection cannot contain duplicates and modes are properly managed.</remarks>
/// <remarks>
/// The collection cannot contain duplicates and modes are properly managed.
/// </remarks>
public class ArtifactDependencyCollection : ICollection<ArtifactDependency>
{
private readonly Dictionary<Udi, ArtifactDependency> _dependencies = new();

/// <inheritdoc />
public int Count => _dependencies.Count;

public IEnumerator<ArtifactDependency> GetEnumerator() => _dependencies.Values.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <inheritdoc />
public bool IsReadOnly => false;

/// <inheritdoc />
public void Add(ArtifactDependency item)
{
if (_dependencies.ContainsKey(item.Udi))
if (item.Mode == ArtifactDependencyMode.Exist &&
_dependencies.TryGetValue(item.Udi, out ArtifactDependency? existingItem) &&
existingItem.Mode == ArtifactDependencyMode.Match)
{
ArtifactDependency exist = _dependencies[item.Udi];
if (item.Mode == ArtifactDependencyMode.Exist || item.Mode == exist.Mode)
{
return;
}
// Don't downgrade dependency mode from Match to Exist
return;
}

_dependencies[item.Udi] = item;
}

/// <inheritdoc />
public void Clear() => _dependencies.Clear();

public bool Contains(ArtifactDependency item) =>
_dependencies.ContainsKey(item.Udi) &&
(_dependencies[item.Udi].Mode == item.Mode || _dependencies[item.Udi].Mode == ArtifactDependencyMode.Match);
/// <inheritdoc />
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);

/// <inheritdoc />
public void CopyTo(ArtifactDependency[] array, int arrayIndex) => _dependencies.Values.CopyTo(array, arrayIndex);

public bool Remove(ArtifactDependency item) => throw new NotSupportedException();
/// <inheritdoc />
public bool Remove(ArtifactDependency item) => _dependencies.Remove(item.Udi);

public bool IsReadOnly => false;
/// <inheritdoc />
public IEnumerator<ArtifactDependency> GetEnumerator() => _dependencies.Values.GetEnumerator();

/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
7 changes: 3 additions & 4 deletions src/Umbraco.Core/Deploy/ArtifactDependencyMode.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
namespace Umbraco.Cms.Core.Deploy;

/// <summary>
/// Indicates the mode of the dependency.
/// Indicates the mode of the dependency.
/// </summary>
public enum ArtifactDependencyMode
{
/// <summary>
/// The dependency must match exactly.
/// The dependency must match exactly.
/// </summary>
Match,

/// <summary>
/// The dependency must exist.
/// The dependency must exist.
/// </summary>
Exist,
}
47 changes: 33 additions & 14 deletions src/Umbraco.Core/Deploy/ArtifactDeployState.cs
Original file line number Diff line number Diff line change
@@ -1,60 +1,73 @@
namespace Umbraco.Cms.Core.Deploy;

/// <summary>
/// Represent the state of an artifact being deployed.
/// Represent the state of an artifact being deployed.
/// </summary>
public abstract class ArtifactDeployState
{
/// <summary>
/// Gets the artifact.
/// Gets the artifact.
/// </summary>
/// <value>
/// The artifact.
/// </value>
public IArtifact Artifact => GetArtifactAsIArtifact();

/// <summary>
/// Gets or sets the service connector in charge of deploying the artifact.
/// Gets or sets the service connector in charge of deploying the artifact.
/// </summary>
/// <value>
/// The connector.
/// </value>
public IServiceConnector? Connector { get; set; }

/// <summary>
/// Gets or sets the next pass number.
/// Gets or sets the next pass number.
/// </summary>
/// <value>
/// The next pass.
/// </value>
public int NextPass { get; set; }

/// <summary>
/// Creates a new instance of the <see cref="ArtifactDeployState" /> class from an artifact and an entity.
/// Creates a new instance of the <see cref="ArtifactDeployState" /> class from an artifact and an entity.
/// </summary>
/// <typeparam name="TArtifact">The type of the artifact.</typeparam>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <param name="art">The artifact.</param>
/// <param name="entity">The entity.</param>
/// <param name="connector">The service connector deploying the artifact.</param>
/// <param name="nextPass">The next pass number.</param>
/// <returns>A deploying artifact.</returns>
/// <returns>
/// A deploying artifact.
/// </returns>
public static ArtifactDeployState<TArtifact, TEntity> Create<TArtifact, TEntity>(TArtifact art, TEntity? entity, IServiceConnector connector, int nextPass)
where TArtifact : IArtifact =>
new ArtifactDeployState<TArtifact, TEntity>(art, entity, connector, nextPass);

/// <summary>
/// Gets the artifact as an <see cref="IArtifact" />.
/// Gets the artifact as an <see cref="IArtifact" />.
/// </summary>
/// <returns>The artifact, as an <see cref="IArtifact" />.</returns>
/// <returns>
/// The artifact, as an <see cref="IArtifact" />.
/// </returns>
/// <remarks>
/// This is because classes that inherit from this class cannot override the Artifact property
/// with a property that specializes the return type, and so they need to 'new' the property.
/// This is because classes that inherit from this class cannot override the Artifact property
/// with a property that specializes the return type, and so they need to 'new' the property.
/// </remarks>
protected abstract IArtifact GetArtifactAsIArtifact();
}

/// <summary>
/// Represent the state of an artifact being deployed.
/// Represent the state of an artifact being deployed.
/// </summary>
/// <typeparam name="TArtifact">The type of the artifact.</typeparam>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
public class ArtifactDeployState<TArtifact, TEntity> : ArtifactDeployState
where TArtifact : IArtifact
{
/// <summary>
/// Initializes a new instance of the <see cref="ArtifactDeployState{TArtifact,TEntity}" /> class.
/// Initializes a new instance of the <see cref="ArtifactDeployState{TArtifact,TEntity}" /> class.
/// </summary>
/// <param name="art">The artifact.</param>
/// <param name="entity">The entity.</param>
Expand All @@ -69,13 +82,19 @@ public ArtifactDeployState(TArtifact art, TEntity? entity, IServiceConnector con
}

/// <summary>
/// Gets or sets the artifact.
/// Gets or sets the artifact.
/// </summary>
/// <value>
/// The artifact.
/// </value>
public new TArtifact Artifact { get; set; }

/// <summary>
/// Gets or sets the entity.
/// Gets or sets the entity.
/// </summary>
/// <value>
/// The entity.
/// </value>
public TEntity? Entity { get; set; }

/// <inheritdoc />
Expand Down
12 changes: 11 additions & 1 deletion src/Umbraco.Core/Deploy/ArtifactSignature.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
namespace Umbraco.Cms.Core.Deploy;

/// <inheritdoc />
public sealed class ArtifactSignature : IArtifactSignature
{
/// <summary>
/// Initializes a new instance of the <see cref="ArtifactSignature" /> class.
/// </summary>
/// <param name="udi">The UDI.</param>
/// <param name="checksum">The checksum.</param>
/// <param name="dependencies">The artifact dependencies.</param>
public ArtifactSignature(Udi udi, string checksum, IEnumerable<ArtifactDependency>? dependencies = null)
{
Udi = udi;
Checksum = checksum;
Dependencies = dependencies ?? Enumerable.Empty<ArtifactDependency>();
Dependencies = dependencies ?? Array.Empty<ArtifactDependency>();
Comment thread
AndyButland marked this conversation as resolved.
}

/// <inheritdoc />
public Udi Udi { get; }

/// <inheritdoc />
public string Checksum { get; }

/// <inheritdoc />
public IEnumerable<ArtifactDependency> Dependencies { get; }
}
Loading