From 2d850bf8f1badd08f6ad5ab9e238684e8de14a71 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 15 Apr 2024 18:04:47 +0200 Subject: [PATCH] v14: Add async methods to Deploy interfaces (#16055) * Add async method to IContextCache * Add async methods to IImageSourceParser and ILocalLinkParser * Add async methods to IDataTypeConfigurationConnector, IServiceConnector and IValueConnector * Obsolete non-aysnc methods * Add cancellation tokens to new async methods * Add ConfigureAwait(false) to awaits --- src/Umbraco.Core/Deploy/IContextCache.cs | 29 ++++ .../Deploy/IDataTypeConfigurationConnector.cs | 38 ++++- src/Umbraco.Core/Deploy/IFileSource.cs | 4 + src/Umbraco.Core/Deploy/IFileType.cs | 2 + src/Umbraco.Core/Deploy/IImageSourceParser.cs | 37 ++++ src/Umbraco.Core/Deploy/ILocalLinkParser.cs | 40 ++++- src/Umbraco.Core/Deploy/IServiceConnector.cs | 158 ++++++++++++++++-- src/Umbraco.Core/Deploy/IValueConnector.cs | 34 ++++ src/Umbraco.Core/Deploy/PassThroughCache.cs | 4 + 9 files changed, 324 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Core/Deploy/IContextCache.cs b/src/Umbraco.Core/Deploy/IContextCache.cs index d175064905..8d3fd6a641 100644 --- a/src/Umbraco.Core/Deploy/IContextCache.cs +++ b/src/Umbraco.Core/Deploy/IContextCache.cs @@ -24,6 +24,35 @@ public interface IContextCache /// T? GetOrCreate(string key, Func factory); + /// + /// Gets an item from the context cache or creates and stores it using the specified . + /// + /// The type of the cached item. + /// The key of the cached item. + /// The factory method to create the item (if it doesn't exist yet). + /// + /// A task that represents the asynchronous operation. The task result contains the item. + /// + async Task GetOrCreateAsync(string key, Func> factory) + { + // TODO: Remove default implementation in v15 + bool shouldCreate = false; + T? value = GetOrCreate(key, () => + { + shouldCreate = true; + return default; + }); + + if (shouldCreate) + { + // Only invoke and await if we need to create the value + value = await factory().ConfigureAwait(false); + Create(key, value); + } + + return value; + } + /// /// Clears all cached items on this context. /// diff --git a/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs b/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs index a293dfd897..052d3df388 100644 --- a/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs +++ b/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs @@ -3,11 +3,11 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Deploy; /// -/// Defines methods that can convert data type configuration to / from an environment-agnostic string. +/// Defines methods that can convert data type configuration to and from an environment-agnostic string. /// /// -/// Configuration may contain values such as content identifiers, that would be local -/// to one environment, and need to be converted in order to be deployed. +/// Configuration may contain values such as content identifiers, that would be local to one environment, and need to be converted in order to be deployed. +/// It can also contain references to other deployable artifacts, that need to be tracked as dependencies. /// public interface IDataTypeConfigurationConnector { @@ -28,8 +28,24 @@ public interface IDataTypeConfigurationConnector /// /// The artifact configuration value. /// + [Obsolete("Use ToArtifactAsync() instead. This method will be removed in a future version.")] string? ToArtifact(IDataType dataType, ICollection dependencies, IContextCache contextCache); + /// + /// Gets the artifact configuration value corresponding to a data type configuration and gather dependencies. + /// + /// The data type. + /// The dependencies. + /// The context cache. + /// The cancellation token. + /// + /// A task that represents the asynchronous operation. The task result contains the artifact configuration value. + /// + Task ToArtifactAsync(IDataType dataType, ICollection dependencies, IContextCache contextCache, CancellationToken cancellationToken = default) +#pragma warning disable CS0618 // Type or member is obsolete + => Task.FromResult(ToArtifact(dataType, dependencies, contextCache)); // TODO: Remove default implementation in v15 +#pragma warning restore CS0618 // Type or member is obsolete + /// /// Gets the data type configuration corresponding to an artifact configuration value. /// @@ -39,5 +55,21 @@ public interface IDataTypeConfigurationConnector /// /// The data type configuration. /// + [Obsolete("Use FromArtifactAsync() instead. This method will be removed in a future version.")] IDictionary FromArtifact(IDataType dataType, string? configuration, IContextCache contextCache); + + /// + /// Gets the data type configuration corresponding to an artifact configuration value. + /// + /// The data type. + /// The artifact configuration value. + /// The context cache. + /// The cancellation token. + /// + /// A task that represents the asynchronous operation. The task result contains the data type configuration. + /// + Task> FromArtifactAsync(IDataType dataType, string? configuration, IContextCache contextCache, CancellationToken cancellationToken = default) +#pragma warning disable CS0618 // Type or member is obsolete + => Task.FromResult(FromArtifact(dataType, configuration, contextCache)); // TODO: Remove default implementation in v15 +#pragma warning restore CS0618 // Type or member is obsolete } diff --git a/src/Umbraco.Core/Deploy/IFileSource.cs b/src/Umbraco.Core/Deploy/IFileSource.cs index 5797f841db..53db3e26e6 100644 --- a/src/Umbraco.Core/Deploy/IFileSource.cs +++ b/src/Umbraco.Core/Deploy/IFileSource.cs @@ -17,6 +17,7 @@ public interface IFileSource /// Returns null if no content could be read. /// The caller should ensure that the stream is properly closed/disposed. /// + [Obsolete("Use GetFileStreamAsync() instead. This method will be removed in a future version.")] Stream GetFileStream(StringUdi udi); /// @@ -43,6 +44,7 @@ public interface IFileSource /// /// Returns null if no content could be read. /// + [Obsolete("Use GetFileContentAsync() instead. This method will be removed in a future version.")] string GetFileContent(StringUdi udi); /// @@ -65,6 +67,7 @@ public interface IFileSource /// /// The length of the file, or -1 if the file does not exist. /// + [Obsolete("Use GetFileLengthAsync() instead. This method will be removed in a future version.")] long GetFileLength(StringUdi udi); /// @@ -83,6 +86,7 @@ public interface IFileSource /// The UDIs of the files to get. /// A flag indicating whether to continue if a file isn't found or to stop and throw a FileNotFoundException. /// A collection of file types which can store the files. + [Obsolete("Use GetFilesAsync() instead. This method will be removed in a future version.")] void GetFiles(IEnumerable udis, bool continueOnFileNotFound, IFileTypeCollection fileTypes); /// diff --git a/src/Umbraco.Core/Deploy/IFileType.cs b/src/Umbraco.Core/Deploy/IFileType.cs index dfd80e2c1e..a135481eb4 100644 --- a/src/Umbraco.Core/Deploy/IFileType.cs +++ b/src/Umbraco.Core/Deploy/IFileType.cs @@ -20,6 +20,7 @@ public interface IFileType /// /// The stream. /// + [Obsolete("Use GetStreamAsync() instead. This method will be removed in a future version.")] Stream GetStream(StringUdi udi); /// @@ -55,6 +56,7 @@ public interface IFileType /// /// The UDI. /// The stream. + [Obsolete("Use SetStreamAsync() instead. This method will be removed in a future version.")] void SetStream(StringUdi udi, Stream stream); /// diff --git a/src/Umbraco.Core/Deploy/IImageSourceParser.cs b/src/Umbraco.Core/Deploy/IImageSourceParser.cs index a05cc897e8..3817dc6fb8 100644 --- a/src/Umbraco.Core/Deploy/IImageSourceParser.cs +++ b/src/Umbraco.Core/Deploy/IImageSourceParser.cs @@ -17,8 +17,27 @@ public interface IImageSourceParser /// /// Turns src="/media/..." into src="umb://media/..." and adds the corresponding udi to the dependencies. /// + [Obsolete("Use ToArtifactAsync() instead. This method will be removed in a future version.")] string ToArtifact(string value, ICollection dependencies, IContextCache contextCache); + /// + /// Parses an Umbraco property value and produces an artifact property value. + /// + /// The property value. + /// A list of dependencies. + /// The context cache. + /// The cancellation token. + /// + /// A task that represents the asynchronous operation. The task result contains the parsed value. + /// + /// + /// Turns src="/media/..." into src="umb://media/..." and adds the corresponding udi to the dependencies. + /// + Task ToArtifactAsync(string value, ICollection dependencies, IContextCache contextCache, CancellationToken cancellationToken = default) +#pragma warning disable CS0618 // Type or member is obsolete + => Task.FromResult(ToArtifact(value, dependencies, contextCache)); // TODO: Remove default implementation in v15 +#pragma warning restore CS0618 // Type or member is obsolete + /// /// Parses an artifact property value and produces an Umbraco property value. /// @@ -30,5 +49,23 @@ public interface IImageSourceParser /// /// Turns umb://media/... into /media/.... /// + [Obsolete("Use FromArtifactAsync() instead. This method will be removed in a future version.")] string FromArtifact(string value, IContextCache contextCache); + + /// + /// Parses an artifact property value and produces an Umbraco property value. + /// + /// The artifact property value. + /// The context cache. + /// The cancellation token. + /// + /// A task that represents the asynchronous operation. The task result contains the parsed value. + /// + /// + /// Turns umb://media/... into /media/.... + /// + Task FromArtifactAsync(string value, IContextCache contextCache, CancellationToken cancellationToken = default) +#pragma warning disable CS0618 // Type or member is obsolete + => Task.FromResult(FromArtifact(value, contextCache)); // TODO: Remove default implementation in v15 +#pragma warning restore CS0618 // Type or member is obsolete } diff --git a/src/Umbraco.Core/Deploy/ILocalLinkParser.cs b/src/Umbraco.Core/Deploy/ILocalLinkParser.cs index 970609459c..fcd3405a3c 100644 --- a/src/Umbraco.Core/Deploy/ILocalLinkParser.cs +++ b/src/Umbraco.Core/Deploy/ILocalLinkParser.cs @@ -15,11 +15,29 @@ public interface ILocalLinkParser /// The parsed value. /// /// - /// Turns {{localLink:1234}} into {{localLink:umb://{type}/{id}}} and adds the corresponding udi to the - /// dependencies. + /// Turns {{localLink:1234}} into {{localLink:umb://{type}/{id}}} and adds the corresponding udi to the dependencies. /// + [Obsolete("Use ToArtifactAsync() instead. This method will be removed in a future version.")] string ToArtifact(string value, ICollection dependencies, IContextCache contextCache); + /// + /// Parses an Umbraco property value and produces an artifact property value. + /// + /// The property value. + /// A list of dependencies. + /// The context cache. + /// The cancellation token. + /// + /// A task that represents the asynchronous operation. The task result contains the parsed value. + /// + /// + /// Turns {{localLink:1234}} into {{localLink:umb://{type}/{id}}} and adds the corresponding udi to the dependencies. + /// + Task ToArtifactAsync(string value, ICollection dependencies, IContextCache contextCache, CancellationToken cancellationToken = default) +#pragma warning disable CS0618 // Type or member is obsolete + => Task.FromResult(ToArtifact(value, dependencies, contextCache)); // TODO: Remove default implementation in v15 +#pragma warning restore CS0618 // Type or member is obsolete + /// /// Parses an artifact property value and produces an Umbraco property value. /// @@ -31,5 +49,23 @@ public interface ILocalLinkParser /// /// Turns {{localLink:umb://{type}/{id}}} into {{localLink:1234}}. /// + [Obsolete("Use FromArtifactAsync() instead. This method will be removed in a future version.")] string FromArtifact(string value, IContextCache contextCache); + + /// + /// Parses an artifact property value and produces an Umbraco property value. + /// + /// The artifact property value. + /// The context cache. + /// The cancellation token. + /// + /// A task that represents the asynchronous operation. The task result contains the parsed value. + /// + /// + /// Turns {{localLink:umb://{type}/{id}}} into {{localLink:1234}}. + /// + Task FromArtifactAsync(string value, IContextCache contextCache, CancellationToken cancellationToken = default) +#pragma warning disable CS0618 // Type or member is obsolete + => Task.FromResult(FromArtifact(value, contextCache)); // TODO: Remove default implementation in v15 +#pragma warning restore CS0618 // Type or member is obsolete } diff --git a/src/Umbraco.Core/Deploy/IServiceConnector.cs b/src/Umbraco.Core/Deploy/IServiceConnector.cs index e831ed9e08..79666f2fa7 100644 --- a/src/Umbraco.Core/Deploy/IServiceConnector.cs +++ b/src/Umbraco.Core/Deploy/IServiceConnector.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Deploy; @@ -13,10 +14,25 @@ public interface IServiceConnector : IDiscoverable /// The entity identifier of the artifact. /// The context cache. /// - /// The corresponding artifact, or null. + /// The corresponding artifact or null. /// + [Obsolete("Use GetArtifactAsync() instead. This method will be removed in a future version.")] IArtifact? GetArtifact(Udi udi, IContextCache contextCache); + /// + /// Gets an artifact. + /// + /// The entity identifier of the artifact. + /// The context cache. + /// The cancellation token. + /// + /// A task that represents the asynchronous operation. The task result contains the corresponding artifact or null. + /// + Task GetArtifactAsync(Udi udi, IContextCache contextCache, CancellationToken cancellationToken = default) +#pragma warning disable CS0618 // Type or member is obsolete + => Task.FromResult(GetArtifact(udi, contextCache)); // TODO: Remove default implementation in v15 +#pragma warning restore CS0618 // Type or member is obsolete + /// /// Gets an artifact. /// @@ -25,46 +41,132 @@ public interface IServiceConnector : IDiscoverable /// /// The corresponding artifact. /// + [Obsolete("Use GetArtifactAsync() instead. This method will be removed in a future version.")] IArtifact GetArtifact(object entity, IContextCache contextCache); + /// + /// Gets an artifact. + /// + /// The entity. + /// The context cache. + /// The cancellation token. + /// + /// A task that represents the asynchronous operation. The task result contains the corresponding artifact. + /// + Task GetArtifactAsync(object entity, IContextCache contextCache, CancellationToken cancellationToken = default) +#pragma warning disable CS0618 // Type or member is obsolete + => Task.FromResult(GetArtifact(entity, contextCache)); // TODO: Remove default implementation in v15 +#pragma warning restore CS0618 // Type or member is obsolete + /// /// Initializes processing for an artifact. /// /// The artifact. /// The deploy context. /// - /// The mapped artifact. + /// The state of an artifact being deployed. /// + [Obsolete("Use ProcessInitAsync() instead. This method will be removed in a future version.")] ArtifactDeployState ProcessInit(IArtifact art, IDeployContext context); + /// + /// Initializes processing for an artifact. + /// + /// The artifact. + /// The deploy context. + /// The cancellation token. + /// + /// A task that represents the asynchronous operation. The task result contains the state of an artifact being deployed. + /// + Task ProcessInitAsync(IArtifact artifact, IDeployContext context, CancellationToken cancellationToken = default) +#pragma warning disable CS0618 // Type or member is obsolete + => Task.FromResult(ProcessInit(artifact, context)); // TODO: Remove default implementation in v15 +#pragma warning restore CS0618 // Type or member is obsolete + /// /// Processes an artifact. /// - /// The mapped artifact. + /// The state of the artifact being deployed. /// The deploy context. /// The processing pass number. + [Obsolete("Use ProcessAsync() instead. This method will be removed in a future version.")] void Process(ArtifactDeployState dart, IDeployContext context, int pass); /// - /// Explodes a range into UDIs. + /// Processes an artifact. /// - /// The range. - /// The list of UDIs where to add the new UDIs. - /// - /// Also, it's cool to have a method named Explode. Kaboom! - /// + /// The state of the artifact being deployed. + /// The deploy context. + /// The processing pass number. + /// The cancellation token. + /// + /// A task that represents the asynchronous operation. + /// + Task ProcessAsync(ArtifactDeployState state, IDeployContext context, int pass, CancellationToken cancellationToken = default) + { + // TODO: Remove default implementation in v15 +#pragma warning disable CS0618 // Type or member is obsolete + Process(state, context, pass); +#pragma warning restore CS0618 // Type or member is obsolete + + return Task.CompletedTask; + } + + /// + /// Explodes/expands an UDI range into UDIs. + /// + /// The UDI range. + /// The list of UDIs where to add the exploded/expanded UDIs. + [Obsolete("Use ExpandRangeAsync() instead. This method will be removed in a future version.")] void Explode(UdiRange range, List udis); /// - /// Gets a named range for a specified udi and selector. + /// Expands an UDI range into UDIs. /// - /// The udi. + /// The UDI range. + /// The cancellation token. + /// + /// Returns an which when enumerated will asynchronously expand the UDI range into UDIs. + /// + async IAsyncEnumerable ExpandRangeAsync(UdiRange range, [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + // TODO: Remove default implementation in v15 + var udis = new List(); +#pragma warning disable CS0618 // Type or member is obsolete + Explode(range, udis); +#pragma warning restore CS0618 // Type or member is obsolete + + foreach (Udi udi in udis) + { + yield return await ValueTask.FromResult(udi); + } + } + + /// + /// Gets a named range for a specified UDI and selector. + /// + /// The UDI. /// The selector. /// - /// The named range for the specified udi and selector. + /// The named range for the specified UDI and selector. /// + [Obsolete("Use GetRangeAsync() instead. This method will be removed in a future version.")] NamedUdiRange GetRange(Udi udi, string selector); + /// + /// Gets a named range for a specified UDI and selector. + /// + /// The UDI. + /// The selector. + /// The cancellation token. + /// + /// A task that represents the asynchronous operation. The task result contains the named range for the specified UDI and selector. + /// + Task GetRangeAsync(Udi udi, string selector, CancellationToken cancellationToken = default) +#pragma warning disable CS0618 // Type or member is obsolete + => Task.FromResult(GetRange(udi, selector)); // TODO: Remove default implementation in v15 +#pragma warning restore CS0618 // Type or member is obsolete + /// /// Gets a named range for specified entity type, identifier and selector. /// @@ -80,22 +182,44 @@ public interface IServiceConnector : IDiscoverable /// At the moment our UI has a hard time returning proper UDIs, mainly because Core's tree do /// not manage GUIDs but only integers... so we have to provide a way to support it. The string id here /// can be either a real string (for string UDIs) or an "integer as a string", using the value "-1" to - /// indicate the "root" i.e. an open udi. + /// indicate the "root" i.e. an open UDI. /// /// + [Obsolete("Use GetRangeAsync() instead. This method will be removed in a future version.")] NamedUdiRange GetRange(string entityType, string sid, string selector); + /// + /// Gets a named range for specified entity type, identifier and selector. + /// + /// The entity type. + /// The identifier. + /// The selector. + /// The cancellation token. + /// + /// A task that represents the asynchronous operation. The task result contains the named range for the specified entity type, identifier and selector. + /// + /// + /// This is temporary. At least we thought it would be, in sept. 2016. What day is it now? + /// + /// At the moment our UI has a hard time returning proper UDIs, mainly because Core's tree do + /// not manage GUIDs but only integers... so we have to provide a way to support it. The string id here + /// can be either a real string (for string UDIs) or an "integer as a string", using the value "-1" to + /// indicate the "root" i.e. an open UDI. + /// + /// + Task GetRangeAsync(string entityType, string sid, string selector, CancellationToken cancellationToken = default) +#pragma warning disable CS0618 // Type or member is obsolete + => Task.FromResult(GetRange(entityType, sid, selector)); // TODO: Remove default implementation in v15 +#pragma warning restore CS0618 // Type or member is obsolete + /// /// Compares two artifacts. /// /// The first artifact. /// The second artifact. - /// A collection of differences to append to, if not null. + /// A collection of differences to append to, if not null. /// /// A boolean value indicating whether the artifacts are identical. /// - /// - /// ServiceConnectorBase{TArtifact} provides a very basic default implementation. - /// bool Compare(IArtifact? art1, IArtifact? art2, ICollection? differences = null); } diff --git a/src/Umbraco.Core/Deploy/IValueConnector.cs b/src/Umbraco.Core/Deploy/IValueConnector.cs index 5e5e2da1a4..06d37e792e 100644 --- a/src/Umbraco.Core/Deploy/IValueConnector.cs +++ b/src/Umbraco.Core/Deploy/IValueConnector.cs @@ -30,8 +30,25 @@ public interface IValueConnector /// /// The deploy property value. /// + [Obsolete("Use ToArtifactAsync() instead. This method will be removed in a future version.")] string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies, IContextCache contextCache); + /// + /// Gets the deploy property value corresponding to a content property value, and gather dependencies. + /// + /// The content property value. + /// The value property type + /// The content dependencies. + /// The context cache. + /// The cancellation token. + /// + /// A task that represents the asynchronous operation. The task result contains the deploy property value. + /// + Task ToArtifactAsync(object? value, IPropertyType propertyType, ICollection dependencies, IContextCache contextCache, CancellationToken cancellationToken = default) +#pragma warning disable CS0618 // Type or member is obsolete + => Task.FromResult(ToArtifact(value, propertyType, dependencies, contextCache)); // TODO: Remove default implementation in v15 +#pragma warning restore CS0618 // Type or member is obsolete + /// /// Gets the content property value corresponding to a deploy property value. /// @@ -42,5 +59,22 @@ public interface IValueConnector /// /// The content property value. /// + [Obsolete("Use FromArtifactAsync() instead. This method will be removed in a future version.")] object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue, IContextCache contextCache); + + /// + /// Gets the content property value corresponding to a deploy property value. + /// + /// The deploy property value. + /// The value property type + /// The current content property value. + /// The context cache. + /// The cancellation token. + /// + /// A task that represents the asynchronous operation. The task result contains the content property value. + /// + Task FromArtifactAsync(string? value, IPropertyType propertyType, object? currentValue, IContextCache contextCache, CancellationToken cancellationToken = default) +#pragma warning disable CS0618 // Type or member is obsolete + => Task.FromResult(FromArtifact(value, propertyType, currentValue, contextCache)); // TODO: Remove default implementation in v15 +#pragma warning restore CS0618 // Type or member is obsolete } diff --git a/src/Umbraco.Core/Deploy/PassThroughCache.cs b/src/Umbraco.Core/Deploy/PassThroughCache.cs index 064fc10ad5..a2ef38879b 100644 --- a/src/Umbraco.Core/Deploy/PassThroughCache.cs +++ b/src/Umbraco.Core/Deploy/PassThroughCache.cs @@ -28,6 +28,10 @@ public sealed class PassThroughCache : IContextCache public T? GetOrCreate(string key, Func factory) => factory(); + /// + public async Task GetOrCreateAsync(string key, Func> factory) + => await factory().ConfigureAwait(false); + /// public void Clear() { }