From eb0ec7bee89bd677428d9aa70aa87f33cd39f179 Mon Sep 17 00:00:00 2001 From: kjac Date: Mon, 13 Feb 2023 19:03:58 +0100 Subject: [PATCH 01/20] Initial implementation for extension manifests --- .../Package/AllPackagesController.cs | 35 ++++++ .../Package/PackageControllerBase.cs | 12 +++ .../PackageBuilderExtensions.cs | 15 +++ .../ManagementApiComposer.cs | 1 + ...ExtensionManifestViewModelMapDefinition.cs | 19 ++++ .../Serialization/SystemTextJsonSerializer.cs | 1 + .../Package/ExtensionManifestViewModel.cs | 10 ++ .../Manifest/ExtensionManifest.cs | 12 +++ .../Manifest/IExtensionManifestService.cs | 6 ++ .../Telemetry/ITelemetryService.cs | 12 ++- .../Telemetry/TelemetryService.cs | 100 +++++++++++------- .../UmbracoBuilder.CoreServices.cs | 4 +- .../Manifest/ExtensionManifestReader.cs | 90 ++++++++++++++++ .../Manifest/ExtensionManifestService.cs | 12 +++ .../Manifest/IExtensionManifestReader.cs | 6 ++ .../Serialization/ContextualJsonSerializer.cs | 70 ++++++++++++ ...emTextConfigurationEditorJsonSerializer.cs | 77 +------------- .../Telemetry/TelemetryServiceTests.cs | 89 ++++++++-------- 18 files changed, 410 insertions(+), 161 deletions(-) create mode 100644 src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackagesController.cs create mode 100644 src/Umbraco.Cms.Api.Management/Controllers/Package/PackageControllerBase.cs create mode 100644 src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs create mode 100644 src/Umbraco.Cms.Api.Management/Mapping/Package/ExtensionManifestViewModelMapDefinition.cs create mode 100644 src/Umbraco.Cms.Api.Management/ViewModels/Package/ExtensionManifestViewModel.cs create mode 100644 src/Umbraco.Core/Manifest/ExtensionManifest.cs create mode 100644 src/Umbraco.Core/Manifest/IExtensionManifestService.cs create mode 100644 src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs create mode 100644 src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs create mode 100644 src/Umbraco.Infrastructure/Manifest/IExtensionManifestReader.cs create mode 100644 src/Umbraco.Infrastructure/Serialization/ContextualJsonSerializer.cs diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackagesController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackagesController.cs new file mode 100644 index 0000000000..c77b2a2b86 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackagesController.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Common.ViewModels.Pagination; +using Umbraco.Cms.Api.Management.ViewModels.Package; +using Umbraco.Cms.Core.Manifest; +using Umbraco.Cms.Core.Mapping; + +namespace Umbraco.Cms.Api.Management.Controllers.Package; + +public class AllPackagesController : PackageControllerBase +{ + private readonly IExtensionManifestService _extensionManifestService; + private readonly IUmbracoMapper _umbracoMapper; + + public AllPackagesController(IExtensionManifestService extensionManifestService, IUmbracoMapper umbracoMapper) + { + _extensionManifestService = extensionManifestService; + _umbracoMapper = umbracoMapper; + } + + [HttpGet("all")] + [MapToApiVersion("1.0")] + // TODO: proper view model + mapper + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + public async Task>> AllMigrationStatuses(int skip = 0, int take = 100) + { + ExtensionManifest[] extensionManifests = (await _extensionManifestService.GetManifestsAsync()).ToArray(); + return Ok( + new PagedViewModel + { + Items = _umbracoMapper.MapEnumerable(extensionManifests.Skip(skip).Take(take)), + Total = extensionManifests.Length + }); + } +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Package/PackageControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Package/PackageControllerBase.cs new file mode 100644 index 0000000000..8e7684b439 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/Package/PackageControllerBase.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.Routing; + +namespace Umbraco.Cms.Api.Management.Controllers.Package; + +[ApiController] +[VersionedApiBackOfficeRoute("package")] +[ApiExplorerSettings(GroupName = "Package")] +[ApiVersion("1.0")] +public abstract class PackageControllerBase : ManagementApiControllerBase +{ +} diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs new file mode 100644 index 0000000000..7ffe39bc79 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs @@ -0,0 +1,15 @@ +using Umbraco.Cms.Api.Management.Mapping.Package; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Mapping; + +namespace Umbraco.Cms.Api.Management.DependencyInjection; + +internal static class PackageBuilderExtensions +{ + internal static IUmbracoBuilder AddPackages(this IUmbracoBuilder builder) + { + builder.WithCollectionBuilder().Add(); + + return builder; + } +} diff --git a/src/Umbraco.Cms.Api.Management/ManagementApiComposer.cs b/src/Umbraco.Cms.Api.Management/ManagementApiComposer.cs index 30b5f13b86..fc5cc5c492 100644 --- a/src/Umbraco.Cms.Api.Management/ManagementApiComposer.cs +++ b/src/Umbraco.Cms.Api.Management/ManagementApiComposer.cs @@ -39,6 +39,7 @@ public class ManagementApiComposer : IComposer .AddDataTypes() .AddTemplates() .AddLogViewer() + .AddPackages() .AddBackOfficeAuthentication() .AddApiVersioning() .AddSwaggerGen(); diff --git a/src/Umbraco.Cms.Api.Management/Mapping/Package/ExtensionManifestViewModelMapDefinition.cs b/src/Umbraco.Cms.Api.Management/Mapping/Package/ExtensionManifestViewModelMapDefinition.cs new file mode 100644 index 0000000000..c7dac7d881 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Mapping/Package/ExtensionManifestViewModelMapDefinition.cs @@ -0,0 +1,19 @@ +using Umbraco.Cms.Api.Management.ViewModels.Package; +using Umbraco.Cms.Core.Manifest; +using Umbraco.Cms.Core.Mapping; + +namespace Umbraco.Cms.Api.Management.Mapping.Package; + +public class ExtensionManifestViewModelMapDefinition : IMapDefinition +{ + public void DefineMaps(IUmbracoMapper mapper) + => mapper.Define((_, _) => new ExtensionManifestViewModel(), Map); + + // Umbraco.Code.MapAll + private static void Map(ExtensionManifest source, ExtensionManifestViewModel target, MapperContext context) + { + target.Name = source.Name; + target.Version = source.Version; + target.Extensions = source.Extensions; + } +} diff --git a/src/Umbraco.Cms.Api.Management/Serialization/SystemTextJsonSerializer.cs b/src/Umbraco.Cms.Api.Management/Serialization/SystemTextJsonSerializer.cs index fce9df39cd..e423f2ad2c 100644 --- a/src/Umbraco.Cms.Api.Management/Serialization/SystemTextJsonSerializer.cs +++ b/src/Umbraco.Cms.Api.Management/Serialization/SystemTextJsonSerializer.cs @@ -2,6 +2,7 @@ namespace Umbraco.Cms.Api.Management.Serialization; +// TOOD: move this to Infrastructure.Serialization + get rid of ISystemTextJsonSerializer public class SystemTextJsonSerializer : ISystemTextJsonSerializer { private JsonSerializerOptions _jsonSerializerOptions; diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Package/ExtensionManifestViewModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Package/ExtensionManifestViewModel.cs new file mode 100644 index 0000000000..5bdae198c9 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Package/ExtensionManifestViewModel.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Cms.Api.Management.ViewModels.Package; + +public class ExtensionManifestViewModel +{ + public string Name { get; set; } = string.Empty; + + public string? Version { get; set; } + + public object[] Extensions { get; set; } = Array.Empty(); +} diff --git a/src/Umbraco.Core/Manifest/ExtensionManifest.cs b/src/Umbraco.Core/Manifest/ExtensionManifest.cs new file mode 100644 index 0000000000..3b88c8e826 --- /dev/null +++ b/src/Umbraco.Core/Manifest/ExtensionManifest.cs @@ -0,0 +1,12 @@ +namespace Umbraco.Cms.Core.Manifest; + +public class ExtensionManifest +{ + public required string Name { get; set; } + + public string? Version { get; set; } + + public bool AllowTelemetry { get; set; } = true; + + public required object[] Extensions { get; set; } +} diff --git a/src/Umbraco.Core/Manifest/IExtensionManifestService.cs b/src/Umbraco.Core/Manifest/IExtensionManifestService.cs new file mode 100644 index 0000000000..d11d6fd389 --- /dev/null +++ b/src/Umbraco.Core/Manifest/IExtensionManifestService.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Cms.Core.Manifest; + +public interface IExtensionManifestService +{ + Task> GetManifestsAsync(); +} diff --git a/src/Umbraco.Core/Telemetry/ITelemetryService.cs b/src/Umbraco.Core/Telemetry/ITelemetryService.cs index 23b0d154a4..b018cd4c40 100644 --- a/src/Umbraco.Core/Telemetry/ITelemetryService.cs +++ b/src/Umbraco.Core/Telemetry/ITelemetryService.cs @@ -7,8 +7,14 @@ namespace Umbraco.Cms.Core.Telemetry; /// public interface ITelemetryService { - /// - /// Try and get the - /// + [Obsolete("Please use GetTelemetryReportDataAsync. Will be removed in V15.")] bool TryGetTelemetryReportData(out TelemetryReportData? telemetryReportData); + + /// + /// Attempts to get the + /// + /// + /// May return null if the site is in an unknown state. + /// + Task GetTelemetryReportDataAsync(); } diff --git a/src/Umbraco.Core/Telemetry/TelemetryService.cs b/src/Umbraco.Core/Telemetry/TelemetryService.cs index 4ebf1ba0b9..541ee3ea85 100644 --- a/src/Umbraco.Core/Telemetry/TelemetryService.cs +++ b/src/Umbraco.Core/Telemetry/TelemetryService.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Configuration; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -13,82 +15,106 @@ namespace Umbraco.Cms.Core.Telemetry; /// internal class TelemetryService : ITelemetryService { - private readonly IManifestParser _manifestParser; private readonly IMetricsConsentService _metricsConsentService; private readonly ISiteIdentifierService _siteIdentifierService; private readonly IUmbracoVersion _umbracoVersion; private readonly IUsageInformationService _usageInformationService; + private readonly IExtensionManifestService _extensionManifestService; - /// - /// Initializes a new instance of the class. - /// + [Obsolete("Please use the constructor that does not take an IManifestParser. Will be removed in V15.")] public TelemetryService( IManifestParser manifestParser, IUmbracoVersion umbracoVersion, ISiteIdentifierService siteIdentifierService, IUsageInformationService usageInformationService, IMetricsConsentService metricsConsentService) + : this( + manifestParser, + umbracoVersion, + siteIdentifierService, + usageInformationService, + metricsConsentService, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + [Obsolete("Please use the constructor that does not take an IManifestParser. Will be removed in V15.")] + public TelemetryService( + IManifestParser manifestParser, + IUmbracoVersion umbracoVersion, + ISiteIdentifierService siteIdentifierService, + IUsageInformationService usageInformationService, + IMetricsConsentService metricsConsentService, + IExtensionManifestService extensionManifestService) + : this( + umbracoVersion, + siteIdentifierService, + usageInformationService, + metricsConsentService, + extensionManifestService) + { + } + + /// + /// Initializes a new instance of the class. + /// + public TelemetryService( + IUmbracoVersion umbracoVersion, + ISiteIdentifierService siteIdentifierService, + IUsageInformationService usageInformationService, + IMetricsConsentService metricsConsentService, + IExtensionManifestService extensionManifestService) { - _manifestParser = manifestParser; _umbracoVersion = umbracoVersion; _siteIdentifierService = siteIdentifierService; _usageInformationService = usageInformationService; _metricsConsentService = metricsConsentService; + _extensionManifestService = extensionManifestService; + } + + [Obsolete("Please use GetTelemetryReportDataAsync. Will be removed in V15.")] + public bool TryGetTelemetryReportData(out TelemetryReportData? telemetryReportData) + { + telemetryReportData = GetTelemetryReportDataAsync().GetAwaiter().GetResult(); + return telemetryReportData != null; } /// - public bool TryGetTelemetryReportData(out TelemetryReportData? telemetryReportData) + public async Task GetTelemetryReportDataAsync() { if (_siteIdentifierService.TryGetOrCreateSiteIdentifier(out Guid telemetryId) is false) { - telemetryReportData = null; - return false; + return null; } - telemetryReportData = new TelemetryReportData + return new TelemetryReportData { Id = telemetryId, Version = GetVersion(), - Packages = GetPackageTelemetry(), + Packages = await GetPackageTelemetryAsync(), Detailed = _usageInformationService.GetDetailed(), }; - return true; } - private string? GetVersion() + private string? GetVersion() => _metricsConsentService.GetConsentLevel() == TelemetryLevel.Minimal + ? null + : _umbracoVersion.SemanticVersion.ToSemanticStringWithoutBuild(); + + private async Task?> GetPackageTelemetryAsync() { if (_metricsConsentService.GetConsentLevel() == TelemetryLevel.Minimal) { return null; } - return _umbracoVersion.SemanticVersion.ToSemanticStringWithoutBuild(); - } + IEnumerable manifests = await _extensionManifestService.GetManifestsAsync(); - private IEnumerable? GetPackageTelemetry() - { - if (_metricsConsentService.GetConsentLevel() == TelemetryLevel.Minimal) - { - return null; - } - - List packages = new(); - IEnumerable manifests = _manifestParser.GetManifests(); - - foreach (PackageManifest manifest in manifests) - { - if (manifest.AllowPackageTelemetry is false) + return manifests + .Where(manifest => manifest.AllowTelemetry) + .Select(manifest => new PackageTelemetry { - continue; - } - - packages.Add(new PackageTelemetry - { - Name = manifest.PackageName, - Version = manifest.Version ?? string.Empty, + Name = manifest.Name, + Version = manifest.Version ?? string.Empty }); - } - - return packages; } } diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index bcdb6c3eeb..994dac3564 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -117,7 +117,7 @@ public static partial class UmbracoBuilderExtensions builder.Services.AddScoped(); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); @@ -127,6 +127,8 @@ public static partial class UmbracoBuilderExtensions // register manifest parser, will be injected in collection builders where needed builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); // register the manifest filter collection builder (collection is empty by default) builder.ManifestFilters(); diff --git a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs new file mode 100644 index 0000000000..0bddec1f50 --- /dev/null +++ b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs @@ -0,0 +1,90 @@ +using System.Text; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Serialization; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.Manifest; + +internal sealed class ExtensionManifestReader : IExtensionManifestReader +{ + private readonly IManifestFileProviderFactory _manifestFileProviderFactory; + private readonly IJsonSerializer _jsonSerializer; + private readonly ILogger _logger; + + public ExtensionManifestReader(IManifestFileProviderFactory manifestFileProviderFactory, IJsonSerializer jsonSerializer, ILogger logger) + { + _manifestFileProviderFactory = manifestFileProviderFactory; + _jsonSerializer = jsonSerializer; + _logger = logger; + } + + public async Task> GetManifestsAsync() + { + var manifests = new List(); + IFileProvider? manifestFileProvider = _manifestFileProviderFactory.Create(); + + if (manifestFileProvider is null) + { + throw new ArgumentNullException(nameof(manifestFileProvider)); + } + + IFileInfo[] manifestFiles = GetAllManifestFiles(manifestFileProvider, Constants.SystemDirectories.AppPlugins).ToArray(); + foreach (IFileInfo fileInfo in manifestFiles) + { + string fileContent; + await using (Stream stream = fileInfo.CreateReadStream()) + { + using (var reader = new StreamReader(stream, Encoding.UTF8)) + { + fileContent = await reader.ReadToEndAsync(); + } + } + + if (fileContent.IsNullOrWhiteSpace()) + { + continue; + } + + try + { + ExtensionManifest? manifest = _jsonSerializer.Deserialize(fileContent); + if (manifest != null) + { + manifests.Add(manifest); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Unable to load extension manifest file: {FileName}", fileInfo.Name); + } + } + + return manifests; + } + + // get all extension manifest files (recursively) + private static IEnumerable GetAllManifestFiles(IFileProvider fileProvider, string path) + { + foreach (IFileInfo fileInfo in fileProvider.GetDirectoryContents(path)) + { + if (fileInfo.IsDirectory) + { + var virtualPath = WebPath.Combine(path, fileInfo.Name); + + // recursively find nested extension manifest files + foreach (IFileInfo nested in GetAllManifestFiles(fileProvider, virtualPath)) + { + yield return nested; + } + } + // TODO: use the correct file name + else if (fileInfo.Name.InvariantEquals("extension.json") && !string.IsNullOrEmpty(fileInfo.PhysicalPath)) + { + yield return fileInfo; + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs new file mode 100644 index 0000000000..b8ce09884c --- /dev/null +++ b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs @@ -0,0 +1,12 @@ +namespace Umbraco.Cms.Core.Manifest; + +internal sealed class ExtensionManifestService : IExtensionManifestService +{ + private readonly IExtensionManifestReader _extensionManifestReader; + + public ExtensionManifestService(IExtensionManifestReader extensionManifestReader) + => _extensionManifestReader = extensionManifestReader; + + // TODO: cache manifests for the app lifetime + public async Task> GetManifestsAsync() => await _extensionManifestReader.GetManifestsAsync(); +} diff --git a/src/Umbraco.Infrastructure/Manifest/IExtensionManifestReader.cs b/src/Umbraco.Infrastructure/Manifest/IExtensionManifestReader.cs new file mode 100644 index 0000000000..e3668e912b --- /dev/null +++ b/src/Umbraco.Infrastructure/Manifest/IExtensionManifestReader.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Cms.Core.Manifest; + +public interface IExtensionManifestReader +{ + Task> GetManifestsAsync(); +} diff --git a/src/Umbraco.Infrastructure/Serialization/ContextualJsonSerializer.cs b/src/Umbraco.Infrastructure/Serialization/ContextualJsonSerializer.cs new file mode 100644 index 0000000000..0645e3a30e --- /dev/null +++ b/src/Umbraco.Infrastructure/Serialization/ContextualJsonSerializer.cs @@ -0,0 +1,70 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Core.Web; + +namespace Umbraco.Cms.Infrastructure.Serialization; + +// FIXME: move away from Json.NET; this is a temporary fix that attempts to use System.Text.Json for management API operations, Json.NET for other operations +public class ContextualJsonSerializer : IJsonSerializer +{ + private readonly IRequestAccessor _requestAccessor; + private readonly IJsonSerializer _jsonNetSerializer; + private readonly IJsonSerializer _systemTextSerializer; + + public ContextualJsonSerializer(IRequestAccessor requestAccessor) + { + _requestAccessor = requestAccessor; + _jsonNetSerializer = new JsonNetSerializer(); + _systemTextSerializer = new SystemTextJsonSerializer(); + } + + public string Serialize(object? input) => ContextualizedSerializer().Serialize(input); + + public T? Deserialize(string input) => ContextualizedSerializer().Deserialize(input); + + public T? DeserializeSubset(string input, string key) => throw new NotSupportedException(); + + private IJsonSerializer ContextualizedSerializer() + { + try + { + var requestedPath = _requestAccessor.GetRequestUrl()?.AbsolutePath; + if (requestedPath != null) + { + // add white listed paths for the System.Text.Json config serializer here + // - always use it for the new management API + if (requestedPath.Contains("/umbraco/management/api/")) + { + return _systemTextSerializer; + } + } + } + catch (Exception ex) + { + // ignore - this whole thing is a temporary workaround, let's not make a fuss + } + + return _jsonNetSerializer; + } + + private class SystemTextJsonSerializer : IJsonSerializer + { + private JsonSerializerOptions _jsonSerializerOptions; + + public SystemTextJsonSerializer() + { + _jsonSerializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + _jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); + _jsonSerializerOptions.Converters.Add(new JsonObjectConverter()); + } + + public string Serialize(object? input) => JsonSerializer.Serialize(input, _jsonSerializerOptions); + + public T? Deserialize(string input) => JsonSerializer.Deserialize(input, _jsonSerializerOptions); + + public T? DeserializeSubset(string input, string key) => throw new NotSupportedException(); + } +} + + diff --git a/src/Umbraco.Infrastructure/Serialization/SystemTextConfigurationEditorJsonSerializer.cs b/src/Umbraco.Infrastructure/Serialization/SystemTextConfigurationEditorJsonSerializer.cs index 088a16be88..c7fcd456fa 100644 --- a/src/Umbraco.Infrastructure/Serialization/SystemTextConfigurationEditorJsonSerializer.cs +++ b/src/Umbraco.Infrastructure/Serialization/SystemTextConfigurationEditorJsonSerializer.cs @@ -1,10 +1,10 @@ using System.Text.Json; -using System.Text.Json.Nodes; +using System.Text.Json.Serialization; using Umbraco.Cms.Core.Serialization; namespace Umbraco.Cms.Infrastructure.Serialization; -// TODO: clean up all config editor serializers when we can migrate fully to System.Text.Json +// FIXME: clean up all config editor serializers when we can migrate fully to System.Text.Json // - move this implementation to ConfigurationEditorJsonSerializer (delete the old implementation) // - use this implementation as the registered singleton (delete ContextualConfigurationEditorJsonSerializer) // - reuse the JsonObjectConverter implementation from management API (delete the local implementation - pending V12 branch update) @@ -21,9 +21,9 @@ public class SystemTextConfigurationEditorJsonSerializer : IConfigurationEditorJ // in some cases, configs aren't camel cased in the DB, so we have to resort to case insensitive // property name resolving when creating configuration objects (deserializing DB configs) PropertyNameCaseInsensitive = true, - NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString + NumberHandling = JsonNumberHandling.AllowReadingFromString }; - _jsonSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter()); + _jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); _jsonSerializerOptions.Converters.Add(new JsonObjectConverter()); } @@ -32,73 +32,4 @@ public class SystemTextConfigurationEditorJsonSerializer : IConfigurationEditorJ public T? Deserialize(string input) => JsonSerializer.Deserialize(input, _jsonSerializerOptions); public T? DeserializeSubset(string input, string key) => throw new NotSupportedException(); - - // TODO: reuse the JsonObjectConverter implementation from management API - private class JsonObjectConverter : System.Text.Json.Serialization.JsonConverter - { - public override object Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) => - ParseObject(ref reader); - - public override void Write( - Utf8JsonWriter writer, - object objectToWrite, - JsonSerializerOptions options) - { - if (objectToWrite is null) - { - return; - } - - // If an object is equals "new object()", Json.Serialize would recurse forever and cause a stack overflow - // We have no good way of checking if its an empty object - // which is why we try to check if the object has any properties, and thus will be empty. - if (objectToWrite.GetType().Name is "Object" && !objectToWrite.GetType().GetProperties().Any()) - { - writer.WriteStartObject(); - writer.WriteEndObject(); - } - else - { - JsonSerializer.Serialize(writer, objectToWrite, objectToWrite.GetType(), options); - } - } - - private object ParseObject(ref Utf8JsonReader reader) - { - if (reader.TokenType == JsonTokenType.StartArray) - { - var items = new List(); - while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) - { - items.Add(ParseObject(ref reader)); - } - - return items.ToArray(); - } - - if (reader.TokenType == JsonTokenType.StartObject) - { - var jsonNode = JsonNode.Parse(ref reader); - if (jsonNode is JsonObject jsonObject) - { - return jsonObject; - } - } - - return reader.TokenType switch - { - JsonTokenType.True => true, - JsonTokenType.False => false, - JsonTokenType.Number when reader.TryGetInt32(out int i) => i, - JsonTokenType.Number when reader.TryGetInt64(out long l) => l, - JsonTokenType.Number => reader.GetDouble(), - JsonTokenType.String when reader.TryGetDateTime(out DateTime datetime) => datetime, - JsonTokenType.String => reader.GetString()!, - _ => JsonDocument.ParseValue(ref reader).RootElement.Clone() - }; - } - } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs index 83aa368d70..88bcbb710c 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration; @@ -16,132 +13,130 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Telemetry; public class TelemetryServiceTests { [Test] - public void UsesGetOrCreateSiteId() + public async Task UsesGetOrCreateSiteId() { var version = CreateUmbracoVersion(9, 3, 1); var siteIdentifierServiceMock = new Mock(); var usageInformationServiceMock = new Mock(); var sut = new TelemetryService( - Mock.Of(), version, siteIdentifierServiceMock.Object, usageInformationServiceMock.Object, - Mock.Of()); + Mock.Of(), + Mock.Of()); Guid guid; - sut.TryGetTelemetryReportData(out _); + await sut.GetTelemetryReportDataAsync(); siteIdentifierServiceMock.Verify(x => x.TryGetOrCreateSiteIdentifier(out guid), Times.Once); } [Test] - public void SkipsIfCantGetOrCreateId() + public async Task SkipsIfCantGetOrCreateId() { var version = CreateUmbracoVersion(9, 3, 1); var sut = new TelemetryService( - Mock.Of(), version, CreateSiteIdentifierService(false), Mock.Of(), - Mock.Of()); + Mock.Of(), + Mock.Of()); - var result = sut.TryGetTelemetryReportData(out var telemetry); - - Assert.IsFalse(result); - Assert.IsNull(telemetry); + var result = await sut.GetTelemetryReportDataAsync(); + Assert.IsNull(result); } [Test] - public void ReturnsSemanticVersionWithoutBuild() + public async Task ReturnsSemanticVersionWithoutBuild() { var version = CreateUmbracoVersion(9, 1, 1, "-rc", "-ad2f4k2d"); var metricsConsentService = new Mock(); metricsConsentService.Setup(x => x.GetConsentLevel()).Returns(TelemetryLevel.Detailed); var sut = new TelemetryService( - Mock.Of(), version, CreateSiteIdentifierService(), Mock.Of(), - metricsConsentService.Object); + metricsConsentService.Object, + Mock.Of()); - var result = sut.TryGetTelemetryReportData(out var telemetry); + var result = await sut.GetTelemetryReportDataAsync(); - Assert.IsTrue(result); - Assert.AreEqual("9.1.1-rc", telemetry.Version); + Assert.IsNotNull(result); + Assert.AreEqual("9.1.1-rc", result.Version); } [Test] - public void CanGatherPackageTelemetry() + public async Task CanGatherPackageTelemetry() { var version = CreateUmbracoVersion(9, 1, 1); var versionPackageName = "VersionPackage"; var packageVersion = "1.0.0"; var noVersionPackageName = "NoVersionPackage"; - PackageManifest[] manifests = + ExtensionManifest[] manifests = { - new() { PackageName = versionPackageName, Version = packageVersion }, - new() { PackageName = noVersionPackageName }, + new() { Name = versionPackageName, Version = packageVersion, Extensions = Array.Empty()}, + new() { Name = noVersionPackageName, Extensions = Array.Empty() }, }; - var manifestParser = CreateManifestParser(manifests); + var extensionManifestService = CreateExtensionManifestService(manifests); var metricsConsentService = new Mock(); metricsConsentService.Setup(x => x.GetConsentLevel()).Returns(TelemetryLevel.Basic); var sut = new TelemetryService( - manifestParser, version, CreateSiteIdentifierService(), Mock.Of(), - metricsConsentService.Object); + metricsConsentService.Object, + extensionManifestService); - var success = sut.TryGetTelemetryReportData(out var telemetry); + var result = await sut.GetTelemetryReportDataAsync(); - Assert.IsTrue(success); + Assert.IsNotNull(result); Assert.Multiple(() => { - Assert.AreEqual(2, telemetry.Packages.Count()); - var versionPackage = telemetry.Packages.FirstOrDefault(x => x.Name == versionPackageName); + Assert.AreEqual(2, result.Packages.Count()); + var versionPackage = result.Packages.FirstOrDefault(x => x.Name == versionPackageName); Assert.AreEqual(versionPackageName, versionPackage.Name); Assert.AreEqual(packageVersion, versionPackage.Version); - var noVersionPackage = telemetry.Packages.FirstOrDefault(x => x.Name == noVersionPackageName); + var noVersionPackage = result.Packages.FirstOrDefault(x => x.Name == noVersionPackageName); Assert.AreEqual(noVersionPackageName, noVersionPackage.Name); Assert.AreEqual(string.Empty, noVersionPackage.Version); }); } [Test] - public void RespectsAllowPackageTelemetry() + public async Task RespectsAllowPackageTelemetry() { var version = CreateUmbracoVersion(9, 1, 1); - PackageManifest[] manifests = + ExtensionManifest[] manifests = { - new() { PackageName = "DoNotTrack", AllowPackageTelemetry = false }, - new() { PackageName = "TrackingAllowed", AllowPackageTelemetry = true }, + new() { Name = "DoNotTrack", AllowTelemetry = false, Extensions = Array.Empty() }, + new() { Name = "TrackingAllowed", AllowTelemetry = true, Extensions = Array.Empty() }, }; - var manifestParser = CreateManifestParser(manifests); + var extensionManifestService = CreateExtensionManifestService(manifests); var metricsConsentService = new Mock(); metricsConsentService.Setup(x => x.GetConsentLevel()).Returns(TelemetryLevel.Basic); var sut = new TelemetryService( - manifestParser, version, CreateSiteIdentifierService(), Mock.Of(), - metricsConsentService.Object); + metricsConsentService.Object, + extensionManifestService); - var success = sut.TryGetTelemetryReportData(out var telemetry); + var result = await sut.GetTelemetryReportDataAsync(); - Assert.IsTrue(success); + Assert.IsNotNull(result); Assert.Multiple(() => { - Assert.AreEqual(1, telemetry.Packages.Count()); - Assert.AreEqual("TrackingAllowed", telemetry.Packages.First().Name); + Assert.AreEqual(1, result.Packages.Count()); + Assert.AreEqual("TrackingAllowed", result.Packages.First().Name); }); } - private IManifestParser CreateManifestParser(IEnumerable manifests) + private IExtensionManifestService CreateExtensionManifestService(IEnumerable manifests) { - var manifestParserMock = new Mock(); - manifestParserMock.Setup(x => x.GetManifests()).Returns(manifests); - return manifestParserMock.Object; + var mock = new Mock(); + mock.Setup(x => x.GetManifestsAsync()).Returns(Task.FromResult(manifests)); + return mock.Object; } private IUmbracoVersion CreateUmbracoVersion(int major, int minor, int patch, string prerelease = "", string build = "") From b34f0280c5c0043b2e32dd617958b3f45a674c63 Mon Sep 17 00:00:00 2001 From: kjac Date: Tue, 14 Feb 2023 07:03:25 +0100 Subject: [PATCH 02/20] Move System.Text.Json serializer to Infrastructure project --- .../JsonBuilderExtensions.cs | 4 +--- .../ISystemTextJsonSerializer.cs | 7 ------ .../Serialization/SystemTextJsonSerializer.cs | 18 -------------- .../Services/JsonPatchService.cs | 11 +++++---- .../Serialization/ContextualJsonSerializer.cs | 22 +---------------- .../Serialization/SystemTextJsonSerializer.cs | 24 +++++++++++++++++++ 6 files changed, 32 insertions(+), 54 deletions(-) delete mode 100644 src/Umbraco.Cms.Api.Management/Serialization/ISystemTextJsonSerializer.cs delete mode 100644 src/Umbraco.Cms.Api.Management/Serialization/SystemTextJsonSerializer.cs create mode 100644 src/Umbraco.Infrastructure/Serialization/SystemTextJsonSerializer.cs diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/JsonBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/JsonBuilderExtensions.cs index 270038dba3..3322b28978 100644 --- a/src/Umbraco.Cms.Api.Management/DependencyInjection/JsonBuilderExtensions.cs +++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/JsonBuilderExtensions.cs @@ -1,6 +1,5 @@ using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Api.Management.Serialization; using Umbraco.Cms.Api.Management.Services; namespace Umbraco.Cms.Api.Management.DependencyInjection; @@ -10,8 +9,7 @@ public static class JsonBuilderExtensions internal static IUmbracoBuilder AddJson(this IUmbracoBuilder builder) { builder.Services - .AddTransient() - .AddTransient(); + .AddTransient(); return builder; } diff --git a/src/Umbraco.Cms.Api.Management/Serialization/ISystemTextJsonSerializer.cs b/src/Umbraco.Cms.Api.Management/Serialization/ISystemTextJsonSerializer.cs deleted file mode 100644 index 8ec90b6740..0000000000 --- a/src/Umbraco.Cms.Api.Management/Serialization/ISystemTextJsonSerializer.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Umbraco.Cms.Core.Serialization; - -namespace Umbraco.Cms.Api.Management.Serialization; - -public interface ISystemTextJsonSerializer : IJsonSerializer -{ -} diff --git a/src/Umbraco.Cms.Api.Management/Serialization/SystemTextJsonSerializer.cs b/src/Umbraco.Cms.Api.Management/Serialization/SystemTextJsonSerializer.cs deleted file mode 100644 index e423f2ad2c..0000000000 --- a/src/Umbraco.Cms.Api.Management/Serialization/SystemTextJsonSerializer.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text.Json; - -namespace Umbraco.Cms.Api.Management.Serialization; - -// TOOD: move this to Infrastructure.Serialization + get rid of ISystemTextJsonSerializer -public class SystemTextJsonSerializer : ISystemTextJsonSerializer -{ - private JsonSerializerOptions _jsonSerializerOptions; - public SystemTextJsonSerializer() - { - _jsonSerializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - } - public string Serialize(object? input) => JsonSerializer.Serialize(input, _jsonSerializerOptions); - - public T? Deserialize(string input) => JsonSerializer.Deserialize(input, _jsonSerializerOptions); - - public T? DeserializeSubset(string input, string key) => throw new NotSupportedException(); -} diff --git a/src/Umbraco.Cms.Api.Management/Services/JsonPatchService.cs b/src/Umbraco.Cms.Api.Management/Services/JsonPatchService.cs index af10432492..850e4a2a17 100644 --- a/src/Umbraco.Cms.Api.Management/Services/JsonPatchService.cs +++ b/src/Umbraco.Cms.Api.Management/Services/JsonPatchService.cs @@ -2,21 +2,22 @@ using Json.Patch; using Umbraco.Cms.Api.Management.Serialization; using Umbraco.Cms.Api.Management.ViewModels.JsonPatch; +using Umbraco.Cms.Core.Serialization; namespace Umbraco.Cms.Api.Management.Services; public class JsonPatchService : IJsonPatchService { - private readonly ISystemTextJsonSerializer _systemTextJsonSerializer; + private readonly IJsonSerializer _jsonSerializer; - public JsonPatchService(ISystemTextJsonSerializer systemTextJsonSerializer) => _systemTextJsonSerializer = systemTextJsonSerializer; + public JsonPatchService(IJsonSerializer jsonSerializer) => _jsonSerializer = jsonSerializer; public PatchResult? Patch(JsonPatchViewModel[] patchViewModel, object objectToPatch) { - var patchString = _systemTextJsonSerializer.Serialize(patchViewModel); + var patchString = _jsonSerializer.Serialize(patchViewModel); - var docString = _systemTextJsonSerializer.Serialize(objectToPatch); - JsonPatch? patch = _systemTextJsonSerializer.Deserialize(patchString); + var docString = _jsonSerializer.Serialize(objectToPatch); + JsonPatch? patch = _jsonSerializer.Deserialize(patchString); var doc = JsonNode.Parse(docString); return patch?.Apply(doc); } diff --git a/src/Umbraco.Infrastructure/Serialization/ContextualJsonSerializer.cs b/src/Umbraco.Infrastructure/Serialization/ContextualJsonSerializer.cs index 0645e3a30e..2880611ccb 100644 --- a/src/Umbraco.Infrastructure/Serialization/ContextualJsonSerializer.cs +++ b/src/Umbraco.Infrastructure/Serialization/ContextualJsonSerializer.cs @@ -1,6 +1,4 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Web; namespace Umbraco.Cms.Infrastructure.Serialization; @@ -47,24 +45,6 @@ public class ContextualJsonSerializer : IJsonSerializer return _jsonNetSerializer; } - - private class SystemTextJsonSerializer : IJsonSerializer - { - private JsonSerializerOptions _jsonSerializerOptions; - - public SystemTextJsonSerializer() - { - _jsonSerializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - _jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); - _jsonSerializerOptions.Converters.Add(new JsonObjectConverter()); - } - - public string Serialize(object? input) => JsonSerializer.Serialize(input, _jsonSerializerOptions); - - public T? Deserialize(string input) => JsonSerializer.Deserialize(input, _jsonSerializerOptions); - - public T? DeserializeSubset(string input, string key) => throw new NotSupportedException(); - } } diff --git a/src/Umbraco.Infrastructure/Serialization/SystemTextJsonSerializer.cs b/src/Umbraco.Infrastructure/Serialization/SystemTextJsonSerializer.cs new file mode 100644 index 0000000000..17a4e0fc1a --- /dev/null +++ b/src/Umbraco.Infrastructure/Serialization/SystemTextJsonSerializer.cs @@ -0,0 +1,24 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Umbraco.Cms.Core.Serialization; + +namespace Umbraco.Cms.Infrastructure.Serialization; + +public class SystemTextJsonSerializer : IJsonSerializer +{ + private readonly JsonSerializerOptions _jsonSerializerOptions; + + public SystemTextJsonSerializer() + { + _jsonSerializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + _jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); + // we may need to add JsonObjectConverter at some point, but for the time being things work fine without + // _jsonSerializerOptions.Converters.Add(new JsonObjectConverter()); + } + + public string Serialize(object? input) => JsonSerializer.Serialize(input, _jsonSerializerOptions); + + public T? Deserialize(string input) => JsonSerializer.Deserialize(input, _jsonSerializerOptions); + + public T? DeserializeSubset(string input, string key) => throw new NotSupportedException(); +} From bd61ac14e483305b1329e717782084b1ac746114 Mon Sep 17 00:00:00 2001 From: kjac Date: Tue, 14 Feb 2023 10:19:23 +0100 Subject: [PATCH 03/20] Add unit tests --- .../Manifest/ExtensionManifestReader.cs | 54 ++-- .../Manifest/ExtensionManifestReaderTests.cs | 250 ++++++++++++++++++ 2 files changed, 279 insertions(+), 25 deletions(-) create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs diff --git a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs index 0bddec1f50..1665f372d3 100644 --- a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs +++ b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs @@ -23,15 +23,42 @@ internal sealed class ExtensionManifestReader : IExtensionManifestReader public async Task> GetManifestsAsync() { - var manifests = new List(); IFileProvider? manifestFileProvider = _manifestFileProviderFactory.Create(); - if (manifestFileProvider is null) { throw new ArgumentNullException(nameof(manifestFileProvider)); } IFileInfo[] manifestFiles = GetAllManifestFiles(manifestFileProvider, Constants.SystemDirectories.AppPlugins).ToArray(); + return await ParseManifests(manifestFiles); + } + + private static IEnumerable GetAllManifestFiles(IFileProvider fileProvider, string path) + { + // TODO: use the correct file name + const string extensionFileName = "extension.json"; + foreach (IFileInfo fileInfo in fileProvider.GetDirectoryContents(path)) + { + if (fileInfo.IsDirectory) + { + var virtualPath = WebPath.Combine(path, fileInfo.Name); + + // find all extension manifest files recursively + foreach (IFileInfo nested in GetAllManifestFiles(fileProvider, virtualPath)) + { + yield return nested; + } + } + else if (fileInfo.Name.InvariantEquals(extensionFileName)) + { + yield return fileInfo; + } + } + } + + private async Task> ParseManifests(IFileInfo[] manifestFiles) + { + var manifests = new List(); foreach (IFileInfo fileInfo in manifestFiles) { string fileContent; @@ -64,27 +91,4 @@ internal sealed class ExtensionManifestReader : IExtensionManifestReader return manifests; } - - // get all extension manifest files (recursively) - private static IEnumerable GetAllManifestFiles(IFileProvider fileProvider, string path) - { - foreach (IFileInfo fileInfo in fileProvider.GetDirectoryContents(path)) - { - if (fileInfo.IsDirectory) - { - var virtualPath = WebPath.Combine(path, fileInfo.Name); - - // recursively find nested extension manifest files - foreach (IFileInfo nested in GetAllManifestFiles(fileProvider, virtualPath)) - { - yield return nested; - } - } - // TODO: use the correct file name - else if (fileInfo.Name.InvariantEquals("extension.json") && !string.IsNullOrEmpty(fileInfo.PhysicalPath)) - { - yield return fileInfo; - } - } - } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs new file mode 100644 index 0000000000..1fa766a3c3 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs @@ -0,0 +1,250 @@ +using System.Text; +using System.Text.Json; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Logging; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Manifest; +using Umbraco.Cms.Infrastructure.Serialization; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Manifest; + +[TestFixture] +public class ExtensionManifestReaderTests +{ + private IExtensionManifestReader _reader; + private Mock _rootDirectoryContentsMock; + private Mock> _loggerMock; + private Mock _fileProviderMock; + + [SetUp] + public void SetUp() + { + _rootDirectoryContentsMock = new Mock(); + _fileProviderMock = new Mock(); + _fileProviderMock + .Setup(m => m.GetDirectoryContents(Constants.SystemDirectories.AppPlugins)) + .Returns(_rootDirectoryContentsMock.Object); + var fileProviderFactoryMock = new Mock(); + fileProviderFactoryMock.Setup(m => m.Create()).Returns(_fileProviderMock.Object); + + _loggerMock = new Mock>(); + _reader = new ExtensionManifestReader(fileProviderFactoryMock.Object, new SystemTextJsonSerializer(), _loggerMock.Object); + } + + [Test] + public async Task CanGetManifestAtRoot() + { + _rootDirectoryContentsMock + .Setup(f => f.GetEnumerator()) + .Returns(new List { CreateExtensionManifestFile() }.GetEnumerator()); + + var result = await _reader.GetManifestsAsync(); + Assert.AreEqual(1, result.Count()); + + var first = result.First(); + Assert.AreEqual("My Extension Manifest", first.Name); + Assert.AreEqual("1.2.3", first.Version); + Assert.AreEqual(2, first.Extensions.Count()); + Assert.IsTrue(first.Extensions.All(e => e is JsonElement)); + } + + [Test] + public async Task CanGetManifestsInRootDirectories() + { + var directory1 = CreateDirectoryMock("/my-extension", CreateExtensionManifestFile(DefaultManifestContent("Extension One"))); + var directory2 = CreateDirectoryMock("/my-other-extension", CreateExtensionManifestFile(DefaultManifestContent("Extension Two"))); + _rootDirectoryContentsMock + .Setup(f => f.GetEnumerator()) + .Returns(new List { directory1, directory2 }.GetEnumerator()); + + var result = await _reader.GetManifestsAsync(); + Assert.AreEqual(2, result.Count()); + Assert.AreEqual("Extension One", result.First().Name); + Assert.AreEqual("Extension Two", result.Last().Name); + } + + [Test] + public async Task CanGetManifestsRecursively() + { + var childFolder = CreateDirectoryMock("/my-parent-folder/my-child-folder", CreateExtensionManifestFile(DefaultManifestContent("Nested Extension"))); + var parentFolder = CreateDirectoryMock("/my-parent-folder", childFolder); + + _rootDirectoryContentsMock + .Setup(f => f.GetEnumerator()) + .Returns(new List { parentFolder }.GetEnumerator()); + + var result = await _reader.GetManifestsAsync(); + Assert.AreEqual(1, result.Count()); + Assert.AreEqual("Nested Extension", result.First().Name); + } + + [Test] + public async Task CanSkipEmptyDirectories() + { + var extensionFolder = CreateDirectoryMock("/my-extension-folder", CreateExtensionManifestFile(DefaultManifestContent("My Extension"))); + var emptyFolder = CreateDirectoryMock("/my-empty-folder"); + + _rootDirectoryContentsMock + .Setup(f => f.GetEnumerator()) + .Returns(new List { emptyFolder, extensionFolder }.GetEnumerator()); + + var result = await _reader.GetManifestsAsync(); + Assert.AreEqual(1, result.Count()); + Assert.AreEqual("My Extension", result.First().Name); + } + + [Test] + public async Task CanSkipOtherFiles() + { + var extensionFolder = CreateDirectoryMock( + "/my-extension-folder", + CreateOtherFile("my.js"), + CreateExtensionManifestFile(DefaultManifestContent("My Extension"))); + var otherFolder = CreateDirectoryMock( + "/my-empty-folder", + CreateOtherFile("some.js"), + CreateOtherFile("some.css")); + + _rootDirectoryContentsMock + .Setup(f => f.GetEnumerator()) + .Returns(new List { otherFolder, extensionFolder }.GetEnumerator()); + + var result = await _reader.GetManifestsAsync(); + Assert.AreEqual(1, result.Count()); + Assert.AreEqual("My Extension", result.First().Name); + } + + [Test] + public async Task CanHandleAllEmptyDirectories() + { + var folders = Enumerable.Range(1, 10).Select(i => CreateDirectoryMock($"/my-empty-folder-{i}")).ToList(); + + _rootDirectoryContentsMock + .Setup(f => f.GetEnumerator()) + .Returns(folders.GetEnumerator()); + + var result = await _reader.GetManifestsAsync(); + Assert.AreEqual(0, result.Count()); + } + + [Test] + public async Task CannotGetManifestWithoutName() + { + var content = @"{ + ""version"": ""1.2.3"", + ""allowTelemetry"": true, + ""extensions"": [{ + ""type"": ""tree"" + }, { + ""type"": ""headerApp"" + } + ] +}"; + _rootDirectoryContentsMock + .Setup(f => f.GetEnumerator()) + .Returns(new List { CreateExtensionManifestFile(content) }.GetEnumerator()); + + var result = await _reader.GetManifestsAsync(); + Assert.AreEqual(0, result.Count()); + + EnsureLogErrorWasCalled(); + } + + [Test] + public async Task CannotGetManifestWithoutExtensions() + { + var content = @"{ + ""name"": ""Something"", + ""version"": ""1.2.3"", + ""allowTelemetry"": true +}"; + _rootDirectoryContentsMock + .Setup(f => f.GetEnumerator()) + .Returns(new List { CreateExtensionManifestFile(content) }.GetEnumerator()); + + var result = await _reader.GetManifestsAsync(); + Assert.AreEqual(0, result.Count()); + + EnsureLogErrorWasCalled(); + } + + [TestCase("This is not JSON")] + [TestCase(@"{""name"": ""invalid-json"", ""version"": ")] + public async Task CannotGetInvalidManifest(string content) + { + _rootDirectoryContentsMock + .Setup(f => f.GetEnumerator()) + .Returns(new List { CreateExtensionManifestFile(content) }.GetEnumerator()); + + var result = await _reader.GetManifestsAsync(); + Assert.AreEqual(0, result.Count()); + + EnsureLogErrorWasCalled(); + } + + private void EnsureLogErrorWasCalled(int numberOfTimes = 1) => + _loggerMock.Verify( + x => x.Log( + It.Is(l => l == LogLevel.Error), + It.IsAny(), + It.Is((v, t) => true), + It.IsAny(), + It.Is>((v, t) => true)), + Times.Exactly(numberOfTimes)); + + private IFileInfo CreateDirectoryMock(string path, params IFileInfo[] children) + { + var directoryContentsMock = new Mock(); + directoryContentsMock + .Setup(f => f.GetEnumerator()) + .Returns(children.ToList().GetEnumerator()); + + _fileProviderMock + .Setup(m => m.GetDirectoryContents($"{Constants.SystemDirectories.AppPlugins}{path}")) + .Returns(directoryContentsMock.Object); + + var fileInfo = new Mock(); + fileInfo.SetupGet(f => f.IsDirectory).Returns(true); + fileInfo.SetupGet(f => f.Name).Returns(path.Split(Constants.CharArrays.ForwardSlash).Last()); + + return fileInfo.Object; + } + + private IFileInfo CreateExtensionManifestFile(string? content = null) + { + content ??= DefaultManifestContent(); + + var fileInfo = new Mock(); + fileInfo.SetupGet(f => f.IsDirectory).Returns(false); + fileInfo.SetupGet(f => f.Name).Returns("extension.json"); + fileInfo.Setup(f => f.CreateReadStream()).Returns(new MemoryStream(Encoding.UTF8.GetBytes(content))); + + return fileInfo.Object; + } + + private IFileInfo CreateOtherFile(string name) + { + var fileInfo = new Mock(); + fileInfo.SetupGet(f => f.IsDirectory).Returns(false); + fileInfo.SetupGet(f => f.Name).Returns(name); + fileInfo.Setup(f => f.CreateReadStream()).Returns(new MemoryStream(Encoding.UTF8.GetBytes("this is some file content"))); + + return fileInfo.Object; + } + + private static string DefaultManifestContent(string name = "My Extension Manifest") + => @"{ + ""name"": ""##NAME##"", + ""version"": ""1.2.3"", + ""allowTelemetry"": true, + ""extensions"": [{ + ""type"": ""tree"" + }, { + ""type"": ""headerApp"" + } + ] +}".Replace("##NAME##", name); +} From 341db24ec02134dc320f0b113116658a290fe6fb Mon Sep 17 00:00:00 2001 From: kjac Date: Tue, 14 Feb 2023 10:52:30 +0100 Subject: [PATCH 04/20] Rename manifest reader method --- .../Manifest/ExtensionManifestReader.cs | 2 +- .../Manifest/ExtensionManifestService.cs | 2 +- .../Manifest/IExtensionManifestReader.cs | 2 +- src/Umbraco.Web.UI.New.Client | 2 +- .../Manifest/ExtensionManifestReaderTests.cs | 30 +++++++++---------- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs index 1665f372d3..33d6180fa2 100644 --- a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs +++ b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs @@ -21,7 +21,7 @@ internal sealed class ExtensionManifestReader : IExtensionManifestReader _logger = logger; } - public async Task> GetManifestsAsync() + public async Task> ReadManifestsAsync() { IFileProvider? manifestFileProvider = _manifestFileProviderFactory.Create(); if (manifestFileProvider is null) diff --git a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs index b8ce09884c..19c44dd96f 100644 --- a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs +++ b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs @@ -8,5 +8,5 @@ internal sealed class ExtensionManifestService : IExtensionManifestService => _extensionManifestReader = extensionManifestReader; // TODO: cache manifests for the app lifetime - public async Task> GetManifestsAsync() => await _extensionManifestReader.GetManifestsAsync(); + public async Task> GetManifestsAsync() => await _extensionManifestReader.ReadManifestsAsync(); } diff --git a/src/Umbraco.Infrastructure/Manifest/IExtensionManifestReader.cs b/src/Umbraco.Infrastructure/Manifest/IExtensionManifestReader.cs index e3668e912b..4f62434506 100644 --- a/src/Umbraco.Infrastructure/Manifest/IExtensionManifestReader.cs +++ b/src/Umbraco.Infrastructure/Manifest/IExtensionManifestReader.cs @@ -2,5 +2,5 @@ public interface IExtensionManifestReader { - Task> GetManifestsAsync(); + Task> ReadManifestsAsync(); } diff --git a/src/Umbraco.Web.UI.New.Client b/src/Umbraco.Web.UI.New.Client index 1c04f6d204..6a7979d95a 160000 --- a/src/Umbraco.Web.UI.New.Client +++ b/src/Umbraco.Web.UI.New.Client @@ -1 +1 @@ -Subproject commit 1c04f6d204404df4d271ef70c679500b73d439c7 +Subproject commit 6a7979d95a5014543a6dc555cceaaddb43dd35b4 diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs index 1fa766a3c3..862a8ec2a2 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs @@ -35,13 +35,13 @@ public class ExtensionManifestReaderTests } [Test] - public async Task CanGetManifestAtRoot() + public async Task CanReadManifestAtRoot() { _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) .Returns(new List { CreateExtensionManifestFile() }.GetEnumerator()); - var result = await _reader.GetManifestsAsync(); + var result = await _reader.ReadManifestsAsync(); Assert.AreEqual(1, result.Count()); var first = result.First(); @@ -52,7 +52,7 @@ public class ExtensionManifestReaderTests } [Test] - public async Task CanGetManifestsInRootDirectories() + public async Task CanReadManifestsInRootDirectories() { var directory1 = CreateDirectoryMock("/my-extension", CreateExtensionManifestFile(DefaultManifestContent("Extension One"))); var directory2 = CreateDirectoryMock("/my-other-extension", CreateExtensionManifestFile(DefaultManifestContent("Extension Two"))); @@ -60,14 +60,14 @@ public class ExtensionManifestReaderTests .Setup(f => f.GetEnumerator()) .Returns(new List { directory1, directory2 }.GetEnumerator()); - var result = await _reader.GetManifestsAsync(); + var result = await _reader.ReadManifestsAsync(); Assert.AreEqual(2, result.Count()); Assert.AreEqual("Extension One", result.First().Name); Assert.AreEqual("Extension Two", result.Last().Name); } [Test] - public async Task CanGetManifestsRecursively() + public async Task CanReadManifestsRecursively() { var childFolder = CreateDirectoryMock("/my-parent-folder/my-child-folder", CreateExtensionManifestFile(DefaultManifestContent("Nested Extension"))); var parentFolder = CreateDirectoryMock("/my-parent-folder", childFolder); @@ -76,7 +76,7 @@ public class ExtensionManifestReaderTests .Setup(f => f.GetEnumerator()) .Returns(new List { parentFolder }.GetEnumerator()); - var result = await _reader.GetManifestsAsync(); + var result = await _reader.ReadManifestsAsync(); Assert.AreEqual(1, result.Count()); Assert.AreEqual("Nested Extension", result.First().Name); } @@ -91,7 +91,7 @@ public class ExtensionManifestReaderTests .Setup(f => f.GetEnumerator()) .Returns(new List { emptyFolder, extensionFolder }.GetEnumerator()); - var result = await _reader.GetManifestsAsync(); + var result = await _reader.ReadManifestsAsync(); Assert.AreEqual(1, result.Count()); Assert.AreEqual("My Extension", result.First().Name); } @@ -112,7 +112,7 @@ public class ExtensionManifestReaderTests .Setup(f => f.GetEnumerator()) .Returns(new List { otherFolder, extensionFolder }.GetEnumerator()); - var result = await _reader.GetManifestsAsync(); + var result = await _reader.ReadManifestsAsync(); Assert.AreEqual(1, result.Count()); Assert.AreEqual("My Extension", result.First().Name); } @@ -126,12 +126,12 @@ public class ExtensionManifestReaderTests .Setup(f => f.GetEnumerator()) .Returns(folders.GetEnumerator()); - var result = await _reader.GetManifestsAsync(); + var result = await _reader.ReadManifestsAsync(); Assert.AreEqual(0, result.Count()); } [Test] - public async Task CannotGetManifestWithoutName() + public async Task CannotReadManifestWithoutName() { var content = @"{ ""version"": ""1.2.3"", @@ -147,14 +147,14 @@ public class ExtensionManifestReaderTests .Setup(f => f.GetEnumerator()) .Returns(new List { CreateExtensionManifestFile(content) }.GetEnumerator()); - var result = await _reader.GetManifestsAsync(); + var result = await _reader.ReadManifestsAsync(); Assert.AreEqual(0, result.Count()); EnsureLogErrorWasCalled(); } [Test] - public async Task CannotGetManifestWithoutExtensions() + public async Task CannotReadManifestWithoutExtensions() { var content = @"{ ""name"": ""Something"", @@ -165,7 +165,7 @@ public class ExtensionManifestReaderTests .Setup(f => f.GetEnumerator()) .Returns(new List { CreateExtensionManifestFile(content) }.GetEnumerator()); - var result = await _reader.GetManifestsAsync(); + var result = await _reader.ReadManifestsAsync(); Assert.AreEqual(0, result.Count()); EnsureLogErrorWasCalled(); @@ -173,13 +173,13 @@ public class ExtensionManifestReaderTests [TestCase("This is not JSON")] [TestCase(@"{""name"": ""invalid-json"", ""version"": ")] - public async Task CannotGetInvalidManifest(string content) + public async Task CannotReadInvalidManifest(string content) { _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) .Returns(new List { CreateExtensionManifestFile(content) }.GetEnumerator()); - var result = await _reader.GetManifestsAsync(); + var result = await _reader.ReadManifestsAsync(); Assert.AreEqual(0, result.Count()); EnsureLogErrorWasCalled(); From bdffbb49aefb55e7cac4aa45a951306b36e122fb Mon Sep 17 00:00:00 2001 From: kjac Date: Tue, 14 Feb 2023 11:39:39 +0100 Subject: [PATCH 05/20] Add async caching extension methods --- src/Umbraco.Core/Cache/AppCacheExtensions.cs | 31 +++++++++++++++++++ .../Cache/RuntimeAppCacheTests.cs | 21 +++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/Umbraco.Core/Cache/AppCacheExtensions.cs b/src/Umbraco.Core/Cache/AppCacheExtensions.cs index 0f1f242ed0..d70e293d4a 100644 --- a/src/Umbraco.Core/Cache/AppCacheExtensions.cs +++ b/src/Umbraco.Core/Cache/AppCacheExtensions.cs @@ -61,4 +61,35 @@ public static class AppCacheExtensions return result.TryConvertTo().Result; } + + public static async Task GetCacheItemAsync( + this IAppPolicyCache provider, + string cacheKey, + Func> getCacheItemAsync, + TimeSpan? timeout, + bool isSliding = false, + string[]? dependentFiles = null) + { + var result = provider.Get(cacheKey); + + if (result == null) + { + result = await getCacheItemAsync(); + provider.Insert(cacheKey, () => result, timeout, isSliding, dependentFiles); + } + + return result == null ? default : result.TryConvertTo().Result; + } + + public static async Task InsertCacheItemAsync( + this IAppPolicyCache provider, + string cacheKey, + Func> getCacheItemAsync, + TimeSpan? timeout = null, + bool isSliding = false, + string[]? dependentFiles = null) + { + T value = await getCacheItemAsync(); + provider.Insert(cacheKey, () => value, timeout, isSliding, dependentFiles); + } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/RuntimeAppCacheTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/RuntimeAppCacheTests.cs index 1b7efdd923..4da509f59b 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/RuntimeAppCacheTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/RuntimeAppCacheTests.cs @@ -27,4 +27,25 @@ public abstract class RuntimeAppCacheTests : AppCacheTests Assert.AreEqual(default(DateTime), AppCache.GetCacheItem("DateTimeTest")); Assert.AreEqual(null, AppCache.GetCacheItem("DateTimeTest")); } + + [Test] + public async Task Can_Get_With_Async_Factory() + { + var value = await AppPolicyCache.GetCacheItemAsync("AsyncFactoryGetTest", async () => await GetValueAsync(5), TimeSpan.FromMilliseconds(100)); + Assert.AreEqual(50, value); + } + + [Test] + public async Task Can_Insert_With_Async_Factory() + { + await AppPolicyCache.InsertCacheItemAsync("AsyncFactoryInsertTest", async () => await GetValueAsync(10), TimeSpan.FromMilliseconds(100)); + var value = AppPolicyCache.GetCacheItem("AsyncFactoryInsertTest"); + Assert.AreEqual(100, value); + } + + private static async Task GetValueAsync(int value) + { + await Task.Delay(10); + return value * 10; + } } From ba87025de8f7a59a682da7bef38bac7b8d70560a Mon Sep 17 00:00:00 2001 From: kjac Date: Tue, 14 Feb 2023 11:39:56 +0100 Subject: [PATCH 06/20] Cache manifests in service layer --- .../Manifest/ExtensionManifestService.cs | 21 ++++-- .../Manifest/ExtensionManifestServiceTests.cs | 66 +++++++++++++++++++ 2 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestServiceTests.cs diff --git a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs index 19c44dd96f..01b8787647 100644 --- a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs +++ b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs @@ -1,12 +1,23 @@ -namespace Umbraco.Cms.Core.Manifest; +using Umbraco.Cms.Core.Cache; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.Manifest; internal sealed class ExtensionManifestService : IExtensionManifestService { private readonly IExtensionManifestReader _extensionManifestReader; + private readonly IAppPolicyCache _cache; - public ExtensionManifestService(IExtensionManifestReader extensionManifestReader) - => _extensionManifestReader = extensionManifestReader; + public ExtensionManifestService(IExtensionManifestReader extensionManifestReader, AppCaches appCaches) + { + _extensionManifestReader = extensionManifestReader; + _cache = appCaches.RuntimeCache; + } - // TODO: cache manifests for the app lifetime - public async Task> GetManifestsAsync() => await _extensionManifestReader.ReadManifestsAsync(); + public async Task> GetManifestsAsync() + => await _cache.GetCacheItemAsync( + $"{nameof(ExtensionManifestService)}-Manifests", + async () => await _extensionManifestReader.ReadManifestsAsync(), + TimeSpan.FromMinutes(10)) + ?? Array.Empty(); } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestServiceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestServiceTests.cs new file mode 100644 index 0000000000..364aba4d05 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestServiceTests.cs @@ -0,0 +1,66 @@ +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Manifest; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Manifest; + +[TestFixture] +public class ExtensionManifestServiceTests +{ + private IExtensionManifestService _service; + private Mock _readerMock; + private IAppPolicyCache _runtimeCache; + + [SetUp] + public void SetUp() + { + _readerMock = new Mock(); + _readerMock.Setup(r => r.ReadManifestsAsync()).ReturnsAsync( + new[] + { + new ExtensionManifest { Name = "Test", Extensions = Array.Empty() } + }); + + _runtimeCache = new ObjectCacheAppCache(); + AppCaches appCaches = new AppCaches( + _runtimeCache, + NoAppCache.Instance, + new IsolatedCaches(type => NoAppCache.Instance)); + + _service = new ExtensionManifestService(_readerMock.Object, appCaches); + } + + [Test] + public async Task CachesManifests() + { + var result = await _service.GetManifestsAsync(); + Assert.AreEqual(1, result.Count()); + + var result2 = await _service.GetManifestsAsync(); + Assert.AreEqual(1, result2.Count()); + + var result3 = await _service.GetManifestsAsync(); + Assert.AreEqual(1, result3.Count()); + + _readerMock.Verify(r => r.ReadManifestsAsync(), Times.Exactly(1)); + } + + [Test] + public async Task ReloadsManifestsAfterCacheClear() + { + var result = await _service.GetManifestsAsync(); + Assert.AreEqual(1, result.Count()); + _runtimeCache.Clear(); + + var result2 = await _service.GetManifestsAsync(); + Assert.AreEqual(1, result2.Count()); + _runtimeCache.Clear(); + + var result3 = await _service.GetManifestsAsync(); + Assert.AreEqual(1, result3.Count()); + _runtimeCache.Clear(); + + _readerMock.Verify(r => r.ReadManifestsAsync(), Times.Exactly(3)); + } +} From 92e53c236e8534c6d8e3d5962d98ace6dfaa73e6 Mon Sep 17 00:00:00 2001 From: kjac Date: Tue, 14 Feb 2023 14:22:23 +0100 Subject: [PATCH 07/20] Rename extension file to umbraco-package.json --- src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs | 3 +-- .../Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs index 33d6180fa2..6adc9cffe0 100644 --- a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs +++ b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs @@ -35,8 +35,7 @@ internal sealed class ExtensionManifestReader : IExtensionManifestReader private static IEnumerable GetAllManifestFiles(IFileProvider fileProvider, string path) { - // TODO: use the correct file name - const string extensionFileName = "extension.json"; + const string extensionFileName = "umbraco-package.json"; foreach (IFileInfo fileInfo in fileProvider.GetDirectoryContents(path)) { if (fileInfo.IsDirectory) diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs index 862a8ec2a2..2b76421820 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs @@ -219,7 +219,7 @@ public class ExtensionManifestReaderTests var fileInfo = new Mock(); fileInfo.SetupGet(f => f.IsDirectory).Returns(false); - fileInfo.SetupGet(f => f.Name).Returns("extension.json"); + fileInfo.SetupGet(f => f.Name).Returns("umbraco-package.json"); fileInfo.Setup(f => f.CreateReadStream()).Returns(new MemoryStream(Encoding.UTF8.GetBytes(content))); return fileInfo.Object; From 4087f7805e28eafdda336333b9a96550f806b50b Mon Sep 17 00:00:00 2001 From: kjac Date: Tue, 14 Feb 2023 16:59:08 +0100 Subject: [PATCH 08/20] Rename ExtensionManifest to PluginConfiguration --- .../Package/AllPackagesController.cs | 35 ------- .../Package/AllPluginsController.cs | 34 +++++++ .../PackageBuilderExtensions.cs | 2 +- ...ginConfigurationViewModelMapDefinition.cs} | 7 +- ...del.cs => PluginConfigurationViewModel.cs} | 2 +- ...IPluginConfigurationFileProviderFactory.cs | 10 ++ .../Manifest/IExtensionManifestService.cs | 6 -- .../Plugin/IPluginConfigurationService.cs | 6 ++ .../PluginConfiguration.cs} | 4 +- .../Telemetry/TelemetryService.cs | 15 +-- .../UmbracoBuilder.CoreServices.cs | 6 +- .../Manifest/ExtensionManifestReader.cs | 93 ------------------ .../Manifest/ExtensionManifestService.cs | 23 ----- .../Manifest/IExtensionManifestReader.cs | 6 -- .../Plugin/IPluginConfigurationReader.cs | 8 ++ .../Plugin/PluginConfigurationReader.cs | 98 +++++++++++++++++++ .../Plugin/PluginConfigurationService.cs | 24 +++++ .../UmbracoBuilderExtensions.cs | 1 + .../ContentAndWebRootFileProviderFactory.cs | 2 +- .../Manifest/ExtensionManifestServiceTests.cs | 66 ------------- .../PluginConfigurationReaderTests.cs} | 98 +++++++++---------- .../Plugin/PluginConfigurationServiceTests.cs | 67 +++++++++++++ .../Telemetry/TelemetryServiceTests.cs | 17 ++-- 23 files changed, 327 insertions(+), 303 deletions(-) delete mode 100644 src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackagesController.cs create mode 100644 src/Umbraco.Cms.Api.Management/Controllers/Package/AllPluginsController.cs rename src/Umbraco.Cms.Api.Management/Mapping/Package/{ExtensionManifestViewModelMapDefinition.cs => PluginConfigurationViewModelMapDefinition.cs} (53%) rename src/Umbraco.Cms.Api.Management/ViewModels/Package/{ExtensionManifestViewModel.cs => PluginConfigurationViewModel.cs} (84%) create mode 100644 src/Umbraco.Core/IO/IPluginConfigurationFileProviderFactory.cs delete mode 100644 src/Umbraco.Core/Manifest/IExtensionManifestService.cs create mode 100644 src/Umbraco.Core/Plugin/IPluginConfigurationService.cs rename src/Umbraco.Core/{Manifest/ExtensionManifest.cs => Plugin/PluginConfiguration.cs} (73%) delete mode 100644 src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs delete mode 100644 src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs delete mode 100644 src/Umbraco.Infrastructure/Manifest/IExtensionManifestReader.cs create mode 100644 src/Umbraco.Infrastructure/Plugin/IPluginConfigurationReader.cs create mode 100644 src/Umbraco.Infrastructure/Plugin/PluginConfigurationReader.cs create mode 100644 src/Umbraco.Infrastructure/Plugin/PluginConfigurationService.cs delete mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestServiceTests.cs rename tests/Umbraco.Tests.UnitTests/Umbraco.Core/{Manifest/ExtensionManifestReaderTests.cs => Plugin/PluginConfigurationReaderTests.cs} (60%) create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Plugin/PluginConfigurationServiceTests.cs diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackagesController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackagesController.cs deleted file mode 100644 index c77b2a2b86..0000000000 --- a/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackagesController.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Cms.Api.Common.ViewModels.Pagination; -using Umbraco.Cms.Api.Management.ViewModels.Package; -using Umbraco.Cms.Core.Manifest; -using Umbraco.Cms.Core.Mapping; - -namespace Umbraco.Cms.Api.Management.Controllers.Package; - -public class AllPackagesController : PackageControllerBase -{ - private readonly IExtensionManifestService _extensionManifestService; - private readonly IUmbracoMapper _umbracoMapper; - - public AllPackagesController(IExtensionManifestService extensionManifestService, IUmbracoMapper umbracoMapper) - { - _extensionManifestService = extensionManifestService; - _umbracoMapper = umbracoMapper; - } - - [HttpGet("all")] - [MapToApiVersion("1.0")] - // TODO: proper view model + mapper - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task>> AllMigrationStatuses(int skip = 0, int take = 100) - { - ExtensionManifest[] extensionManifests = (await _extensionManifestService.GetManifestsAsync()).ToArray(); - return Ok( - new PagedViewModel - { - Items = _umbracoMapper.MapEnumerable(extensionManifests.Skip(skip).Take(take)), - Total = extensionManifests.Length - }); - } -} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPluginsController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPluginsController.cs new file mode 100644 index 0000000000..bbae667125 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPluginsController.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Common.ViewModels.Pagination; +using Umbraco.Cms.Api.Management.ViewModels.Package; +using Umbraco.Cms.Core.Plugin; +using Umbraco.Cms.Core.Mapping; + +namespace Umbraco.Cms.Api.Management.Controllers.Package; + +public class AllPluginsController : PackageControllerBase +{ + private readonly IPluginConfigurationService _pluginConfigurationService; + private readonly IUmbracoMapper _umbracoMapper; + + public AllPluginsController(IPluginConfigurationService pluginConfigurationService, IUmbracoMapper umbracoMapper) + { + _pluginConfigurationService = pluginConfigurationService; + _umbracoMapper = umbracoMapper; + } + + [HttpGet("plugins")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + public async Task>> AllPlugins(int skip = 0, int take = 100) + { + PluginConfiguration[] pluginConfigurations = (await _pluginConfigurationService.GetPluginConfigurationsAsync()).ToArray(); + return Ok( + new PagedViewModel + { + Items = _umbracoMapper.MapEnumerable(pluginConfigurations.Skip(skip).Take(take)), + Total = pluginConfigurations.Length + }); + } +} diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs index 7ffe39bc79..55c8439ec0 100644 --- a/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs +++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs @@ -8,7 +8,7 @@ internal static class PackageBuilderExtensions { internal static IUmbracoBuilder AddPackages(this IUmbracoBuilder builder) { - builder.WithCollectionBuilder().Add(); + builder.WithCollectionBuilder().Add(); return builder; } diff --git a/src/Umbraco.Cms.Api.Management/Mapping/Package/ExtensionManifestViewModelMapDefinition.cs b/src/Umbraco.Cms.Api.Management/Mapping/Package/PluginConfigurationViewModelMapDefinition.cs similarity index 53% rename from src/Umbraco.Cms.Api.Management/Mapping/Package/ExtensionManifestViewModelMapDefinition.cs rename to src/Umbraco.Cms.Api.Management/Mapping/Package/PluginConfigurationViewModelMapDefinition.cs index c7dac7d881..18318ab3fa 100644 --- a/src/Umbraco.Cms.Api.Management/Mapping/Package/ExtensionManifestViewModelMapDefinition.cs +++ b/src/Umbraco.Cms.Api.Management/Mapping/Package/PluginConfigurationViewModelMapDefinition.cs @@ -1,16 +1,17 @@ using Umbraco.Cms.Api.Management.ViewModels.Package; +using Umbraco.Cms.Core.Plugin; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Mapping; namespace Umbraco.Cms.Api.Management.Mapping.Package; -public class ExtensionManifestViewModelMapDefinition : IMapDefinition +public class PluginConfigurationViewModelMapDefinition : IMapDefinition { public void DefineMaps(IUmbracoMapper mapper) - => mapper.Define((_, _) => new ExtensionManifestViewModel(), Map); + => mapper.Define((_, _) => new PluginConfigurationViewModel(), Map); // Umbraco.Code.MapAll - private static void Map(ExtensionManifest source, ExtensionManifestViewModel target, MapperContext context) + private static void Map(PluginConfiguration source, PluginConfigurationViewModel target, MapperContext context) { target.Name = source.Name; target.Version = source.Version; diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Package/ExtensionManifestViewModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Package/PluginConfigurationViewModel.cs similarity index 84% rename from src/Umbraco.Cms.Api.Management/ViewModels/Package/ExtensionManifestViewModel.cs rename to src/Umbraco.Cms.Api.Management/ViewModels/Package/PluginConfigurationViewModel.cs index 5bdae198c9..034a2c6239 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Package/ExtensionManifestViewModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Package/PluginConfigurationViewModel.cs @@ -1,6 +1,6 @@ namespace Umbraco.Cms.Api.Management.ViewModels.Package; -public class ExtensionManifestViewModel +public class PluginConfigurationViewModel { public string Name { get; set; } = string.Empty; diff --git a/src/Umbraco.Core/IO/IPluginConfigurationFileProviderFactory.cs b/src/Umbraco.Core/IO/IPluginConfigurationFileProviderFactory.cs new file mode 100644 index 0000000000..221ccb3a5f --- /dev/null +++ b/src/Umbraco.Core/IO/IPluginConfigurationFileProviderFactory.cs @@ -0,0 +1,10 @@ +using Microsoft.Extensions.FileProviders; + +namespace Umbraco.Cms.Core.IO; + +/// +/// Factory for creating instances for providing the umbraco-package.json file. +/// +public interface IPluginConfigurationFileProviderFactory : IFileProviderFactory +{ +} diff --git a/src/Umbraco.Core/Manifest/IExtensionManifestService.cs b/src/Umbraco.Core/Manifest/IExtensionManifestService.cs deleted file mode 100644 index d11d6fd389..0000000000 --- a/src/Umbraco.Core/Manifest/IExtensionManifestService.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Umbraco.Cms.Core.Manifest; - -public interface IExtensionManifestService -{ - Task> GetManifestsAsync(); -} diff --git a/src/Umbraco.Core/Plugin/IPluginConfigurationService.cs b/src/Umbraco.Core/Plugin/IPluginConfigurationService.cs new file mode 100644 index 0000000000..c176c1ba47 --- /dev/null +++ b/src/Umbraco.Core/Plugin/IPluginConfigurationService.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Cms.Core.Plugin; + +public interface IPluginConfigurationService +{ + Task> GetPluginConfigurationsAsync(); +} diff --git a/src/Umbraco.Core/Manifest/ExtensionManifest.cs b/src/Umbraco.Core/Plugin/PluginConfiguration.cs similarity index 73% rename from src/Umbraco.Core/Manifest/ExtensionManifest.cs rename to src/Umbraco.Core/Plugin/PluginConfiguration.cs index 3b88c8e826..f16f0243d6 100644 --- a/src/Umbraco.Core/Manifest/ExtensionManifest.cs +++ b/src/Umbraco.Core/Plugin/PluginConfiguration.cs @@ -1,6 +1,6 @@ -namespace Umbraco.Cms.Core.Manifest; +namespace Umbraco.Cms.Core.Plugin; -public class ExtensionManifest +public class PluginConfiguration { public required string Name { get; set; } diff --git a/src/Umbraco.Core/Telemetry/TelemetryService.cs b/src/Umbraco.Core/Telemetry/TelemetryService.cs index 541ee3ea85..09ab4483de 100644 --- a/src/Umbraco.Core/Telemetry/TelemetryService.cs +++ b/src/Umbraco.Core/Telemetry/TelemetryService.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Plugin; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -19,7 +20,7 @@ internal class TelemetryService : ITelemetryService private readonly ISiteIdentifierService _siteIdentifierService; private readonly IUmbracoVersion _umbracoVersion; private readonly IUsageInformationService _usageInformationService; - private readonly IExtensionManifestService _extensionManifestService; + private readonly IPluginConfigurationService _pluginConfigurationService; [Obsolete("Please use the constructor that does not take an IManifestParser. Will be removed in V15.")] public TelemetryService( @@ -34,7 +35,7 @@ internal class TelemetryService : ITelemetryService siteIdentifierService, usageInformationService, metricsConsentService, - StaticServiceProvider.Instance.GetRequiredService()) + StaticServiceProvider.Instance.GetRequiredService()) { } @@ -45,13 +46,13 @@ internal class TelemetryService : ITelemetryService ISiteIdentifierService siteIdentifierService, IUsageInformationService usageInformationService, IMetricsConsentService metricsConsentService, - IExtensionManifestService extensionManifestService) + IPluginConfigurationService pluginConfigurationService) : this( umbracoVersion, siteIdentifierService, usageInformationService, metricsConsentService, - extensionManifestService) + pluginConfigurationService) { } @@ -63,13 +64,13 @@ internal class TelemetryService : ITelemetryService ISiteIdentifierService siteIdentifierService, IUsageInformationService usageInformationService, IMetricsConsentService metricsConsentService, - IExtensionManifestService extensionManifestService) + IPluginConfigurationService pluginConfigurationService) { _umbracoVersion = umbracoVersion; _siteIdentifierService = siteIdentifierService; _usageInformationService = usageInformationService; _metricsConsentService = metricsConsentService; - _extensionManifestService = extensionManifestService; + _pluginConfigurationService = pluginConfigurationService; } [Obsolete("Please use GetTelemetryReportDataAsync. Will be removed in V15.")] @@ -107,7 +108,7 @@ internal class TelemetryService : ITelemetryService return null; } - IEnumerable manifests = await _extensionManifestService.GetManifestsAsync(); + IEnumerable manifests = await _pluginConfigurationService.GetPluginConfigurationsAsync(); return manifests .Where(manifest => manifest.AllowTelemetry) diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 994dac3564..2b5a4188bf 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -10,6 +10,7 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.DistributedLocking; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Plugin; using Umbraco.Cms.Core.Handlers; using Umbraco.Cms.Core.HealthChecks.NotificationMethods; using Umbraco.Cms.Core.Hosting; @@ -39,6 +40,7 @@ using Umbraco.Cms.Core.Trees; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.DistributedLocking; using Umbraco.Cms.Infrastructure.Examine; +using Umbraco.Cms.Infrastructure.Plugin; using Umbraco.Cms.Infrastructure.HealthChecks; using Umbraco.Cms.Infrastructure.HostedServices; using Umbraco.Cms.Infrastructure.Install; @@ -127,8 +129,8 @@ public static partial class UmbracoBuilderExtensions // register manifest parser, will be injected in collection builders where needed builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); // register the manifest filter collection builder (collection is empty by default) builder.ManifestFilters(); diff --git a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs deleted file mode 100644 index 6adc9cffe0..0000000000 --- a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestReader.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System.Text; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Serialization; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Core.Manifest; - -internal sealed class ExtensionManifestReader : IExtensionManifestReader -{ - private readonly IManifestFileProviderFactory _manifestFileProviderFactory; - private readonly IJsonSerializer _jsonSerializer; - private readonly ILogger _logger; - - public ExtensionManifestReader(IManifestFileProviderFactory manifestFileProviderFactory, IJsonSerializer jsonSerializer, ILogger logger) - { - _manifestFileProviderFactory = manifestFileProviderFactory; - _jsonSerializer = jsonSerializer; - _logger = logger; - } - - public async Task> ReadManifestsAsync() - { - IFileProvider? manifestFileProvider = _manifestFileProviderFactory.Create(); - if (manifestFileProvider is null) - { - throw new ArgumentNullException(nameof(manifestFileProvider)); - } - - IFileInfo[] manifestFiles = GetAllManifestFiles(manifestFileProvider, Constants.SystemDirectories.AppPlugins).ToArray(); - return await ParseManifests(manifestFiles); - } - - private static IEnumerable GetAllManifestFiles(IFileProvider fileProvider, string path) - { - const string extensionFileName = "umbraco-package.json"; - foreach (IFileInfo fileInfo in fileProvider.GetDirectoryContents(path)) - { - if (fileInfo.IsDirectory) - { - var virtualPath = WebPath.Combine(path, fileInfo.Name); - - // find all extension manifest files recursively - foreach (IFileInfo nested in GetAllManifestFiles(fileProvider, virtualPath)) - { - yield return nested; - } - } - else if (fileInfo.Name.InvariantEquals(extensionFileName)) - { - yield return fileInfo; - } - } - } - - private async Task> ParseManifests(IFileInfo[] manifestFiles) - { - var manifests = new List(); - foreach (IFileInfo fileInfo in manifestFiles) - { - string fileContent; - await using (Stream stream = fileInfo.CreateReadStream()) - { - using (var reader = new StreamReader(stream, Encoding.UTF8)) - { - fileContent = await reader.ReadToEndAsync(); - } - } - - if (fileContent.IsNullOrWhiteSpace()) - { - continue; - } - - try - { - ExtensionManifest? manifest = _jsonSerializer.Deserialize(fileContent); - if (manifest != null) - { - manifests.Add(manifest); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Unable to load extension manifest file: {FileName}", fileInfo.Name); - } - } - - return manifests; - } -} diff --git a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs b/src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs deleted file mode 100644 index 01b8787647..0000000000 --- a/src/Umbraco.Infrastructure/Manifest/ExtensionManifestService.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Umbraco.Cms.Core.Cache; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Core.Manifest; - -internal sealed class ExtensionManifestService : IExtensionManifestService -{ - private readonly IExtensionManifestReader _extensionManifestReader; - private readonly IAppPolicyCache _cache; - - public ExtensionManifestService(IExtensionManifestReader extensionManifestReader, AppCaches appCaches) - { - _extensionManifestReader = extensionManifestReader; - _cache = appCaches.RuntimeCache; - } - - public async Task> GetManifestsAsync() - => await _cache.GetCacheItemAsync( - $"{nameof(ExtensionManifestService)}-Manifests", - async () => await _extensionManifestReader.ReadManifestsAsync(), - TimeSpan.FromMinutes(10)) - ?? Array.Empty(); -} diff --git a/src/Umbraco.Infrastructure/Manifest/IExtensionManifestReader.cs b/src/Umbraco.Infrastructure/Manifest/IExtensionManifestReader.cs deleted file mode 100644 index 4f62434506..0000000000 --- a/src/Umbraco.Infrastructure/Manifest/IExtensionManifestReader.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Umbraco.Cms.Core.Manifest; - -public interface IExtensionManifestReader -{ - Task> ReadManifestsAsync(); -} diff --git a/src/Umbraco.Infrastructure/Plugin/IPluginConfigurationReader.cs b/src/Umbraco.Infrastructure/Plugin/IPluginConfigurationReader.cs new file mode 100644 index 0000000000..f1f012dbe9 --- /dev/null +++ b/src/Umbraco.Infrastructure/Plugin/IPluginConfigurationReader.cs @@ -0,0 +1,8 @@ +using Umbraco.Cms.Core.Plugin; + +namespace Umbraco.Cms.Infrastructure.Plugin; + +public interface IPluginConfigurationReader +{ + Task> ReadPluginConfigurationsAsync(); +} diff --git a/src/Umbraco.Infrastructure/Plugin/PluginConfigurationReader.cs b/src/Umbraco.Infrastructure/Plugin/PluginConfigurationReader.cs new file mode 100644 index 0000000000..9fba461a7a --- /dev/null +++ b/src/Umbraco.Infrastructure/Plugin/PluginConfigurationReader.cs @@ -0,0 +1,98 @@ +using System.Text; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Plugin; +using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Serialization; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Plugin; + +internal sealed class PluginConfigurationReader : IPluginConfigurationReader +{ + private readonly IPluginConfigurationFileProviderFactory _pluginConfigurationFileProviderFactory; + private readonly IJsonSerializer _jsonSerializer; + private readonly ILogger _logger; + + public PluginConfigurationReader( + IPluginConfigurationFileProviderFactory pluginConfigurationFileProviderFactory, + IJsonSerializer jsonSerializer, + ILogger logger) + { + _pluginConfigurationFileProviderFactory = pluginConfigurationFileProviderFactory; + _jsonSerializer = jsonSerializer; + _logger = logger; + } + + public async Task> ReadPluginConfigurationsAsync() + { + IFileProvider? fileProvider = _pluginConfigurationFileProviderFactory.Create(); + if (fileProvider is null) + { + throw new ArgumentNullException(nameof(fileProvider)); + } + + IFileInfo[] files = GetAllPluginConfigurationFiles(fileProvider, Constants.SystemDirectories.AppPlugins).ToArray(); + return await ParsePluginConfigurationFiles(files); + } + + private static IEnumerable GetAllPluginConfigurationFiles(IFileProvider fileProvider, string path) + { + const string extensionFileName = "umbraco-package.json"; + foreach (IFileInfo fileInfo in fileProvider.GetDirectoryContents(path)) + { + if (fileInfo.IsDirectory) + { + var virtualPath = WebPath.Combine(path, fileInfo.Name); + + // find all extension package configuration files recursively + foreach (IFileInfo nested in GetAllPluginConfigurationFiles(fileProvider, virtualPath)) + { + yield return nested; + } + } + else if (fileInfo.Name.InvariantEquals(extensionFileName)) + { + yield return fileInfo; + } + } + } + + private async Task> ParsePluginConfigurationFiles(IFileInfo[] files) + { + var pluginConfigurations = new List(); + foreach (IFileInfo fileInfo in files) + { + string fileContent; + await using (Stream stream = fileInfo.CreateReadStream()) + { + using (var reader = new StreamReader(stream, Encoding.UTF8)) + { + fileContent = await reader.ReadToEndAsync(); + } + } + + if (fileContent.IsNullOrWhiteSpace()) + { + continue; + } + + try + { + PluginConfiguration? pluginConfiguration = _jsonSerializer.Deserialize(fileContent); + if (pluginConfiguration != null) + { + pluginConfigurations.Add(pluginConfiguration); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Unable to load plugin configuration file: {FileName}", fileInfo.Name); + } + } + + return pluginConfigurations; + } +} diff --git a/src/Umbraco.Infrastructure/Plugin/PluginConfigurationService.cs b/src/Umbraco.Infrastructure/Plugin/PluginConfigurationService.cs new file mode 100644 index 0000000000..06a8dd10fe --- /dev/null +++ b/src/Umbraco.Infrastructure/Plugin/PluginConfigurationService.cs @@ -0,0 +1,24 @@ +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Plugin; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Plugin; + +internal sealed class PluginConfigurationService : IPluginConfigurationService +{ + private readonly IPluginConfigurationReader _pluginConfigurationReader; + private readonly IAppPolicyCache _cache; + + public PluginConfigurationService(IPluginConfigurationReader pluginConfigurationReader, AppCaches appCaches) + { + _pluginConfigurationReader = pluginConfigurationReader; + _cache = appCaches.RuntimeCache; + } + + public async Task> GetPluginConfigurationsAsync() + => await _cache.GetCacheItemAsync( + $"{nameof(PluginConfigurationService)}-PluginConfigurations", + async () => await _pluginConfigurationReader.ReadPluginConfigurationsAsync(), + TimeSpan.FromMinutes(10)) + ?? Array.Empty(); +} diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index dc94fa3dc8..7efcb3e67d 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -151,6 +151,7 @@ public static partial class UmbracoBuilderExtensions // therefore no need to register it as singleton builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); // Must be added here because DbProviderFactories is netstandard 2.1 so cannot exist in Infra for now builder.Services.AddSingleton(factory => new DbProviderFactoryCreator( diff --git a/src/Umbraco.Web.Common/FileProviders/ContentAndWebRootFileProviderFactory.cs b/src/Umbraco.Web.Common/FileProviders/ContentAndWebRootFileProviderFactory.cs index 7c3831b919..64263075d8 100644 --- a/src/Umbraco.Web.Common/FileProviders/ContentAndWebRootFileProviderFactory.cs +++ b/src/Umbraco.Web.Common/FileProviders/ContentAndWebRootFileProviderFactory.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.IO; namespace Umbraco.Cms.Web.Common.FileProviders; -public class ContentAndWebRootFileProviderFactory : IManifestFileProviderFactory +public class ContentAndWebRootFileProviderFactory : IManifestFileProviderFactory, IPluginConfigurationFileProviderFactory { private readonly IWebHostEnvironment _webHostEnvironment; diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestServiceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestServiceTests.cs deleted file mode 100644 index 364aba4d05..0000000000 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestServiceTests.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Manifest; - -namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Manifest; - -[TestFixture] -public class ExtensionManifestServiceTests -{ - private IExtensionManifestService _service; - private Mock _readerMock; - private IAppPolicyCache _runtimeCache; - - [SetUp] - public void SetUp() - { - _readerMock = new Mock(); - _readerMock.Setup(r => r.ReadManifestsAsync()).ReturnsAsync( - new[] - { - new ExtensionManifest { Name = "Test", Extensions = Array.Empty() } - }); - - _runtimeCache = new ObjectCacheAppCache(); - AppCaches appCaches = new AppCaches( - _runtimeCache, - NoAppCache.Instance, - new IsolatedCaches(type => NoAppCache.Instance)); - - _service = new ExtensionManifestService(_readerMock.Object, appCaches); - } - - [Test] - public async Task CachesManifests() - { - var result = await _service.GetManifestsAsync(); - Assert.AreEqual(1, result.Count()); - - var result2 = await _service.GetManifestsAsync(); - Assert.AreEqual(1, result2.Count()); - - var result3 = await _service.GetManifestsAsync(); - Assert.AreEqual(1, result3.Count()); - - _readerMock.Verify(r => r.ReadManifestsAsync(), Times.Exactly(1)); - } - - [Test] - public async Task ReloadsManifestsAfterCacheClear() - { - var result = await _service.GetManifestsAsync(); - Assert.AreEqual(1, result.Count()); - _runtimeCache.Clear(); - - var result2 = await _service.GetManifestsAsync(); - Assert.AreEqual(1, result2.Count()); - _runtimeCache.Clear(); - - var result3 = await _service.GetManifestsAsync(); - Assert.AreEqual(1, result3.Count()); - _runtimeCache.Clear(); - - _readerMock.Verify(r => r.ReadManifestsAsync(), Times.Exactly(3)); - } -} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Plugin/PluginConfigurationReaderTests.cs similarity index 60% rename from tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Core/Plugin/PluginConfigurationReaderTests.cs index 2b76421820..0b7e5c982f 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ExtensionManifestReaderTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Plugin/PluginConfigurationReaderTests.cs @@ -6,17 +6,17 @@ using Moq; using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Core.Manifest; +using Umbraco.Cms.Infrastructure.Plugin; using Umbraco.Cms.Infrastructure.Serialization; -namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Manifest; +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Plugin; [TestFixture] -public class ExtensionManifestReaderTests +public class PluginConfigurationReaderTests { - private IExtensionManifestReader _reader; + private IPluginConfigurationReader _reader; private Mock _rootDirectoryContentsMock; - private Mock> _loggerMock; + private Mock> _loggerMock; private Mock _fileProviderMock; [SetUp] @@ -27,82 +27,82 @@ public class ExtensionManifestReaderTests _fileProviderMock .Setup(m => m.GetDirectoryContents(Constants.SystemDirectories.AppPlugins)) .Returns(_rootDirectoryContentsMock.Object); - var fileProviderFactoryMock = new Mock(); + var fileProviderFactoryMock = new Mock(); fileProviderFactoryMock.Setup(m => m.Create()).Returns(_fileProviderMock.Object); - _loggerMock = new Mock>(); - _reader = new ExtensionManifestReader(fileProviderFactoryMock.Object, new SystemTextJsonSerializer(), _loggerMock.Object); + _loggerMock = new Mock>(); + _reader = new PluginConfigurationReader(fileProviderFactoryMock.Object, new SystemTextJsonSerializer(), _loggerMock.Object); } [Test] - public async Task CanReadManifestAtRoot() + public async Task Can_Read_PluginConfigurations_At_Root() { _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) - .Returns(new List { CreateExtensionManifestFile() }.GetEnumerator()); + .Returns(new List { CreatePluginConfigurationFile() }.GetEnumerator()); - var result = await _reader.ReadManifestsAsync(); + var result = await _reader.ReadPluginConfigurationsAsync(); Assert.AreEqual(1, result.Count()); var first = result.First(); - Assert.AreEqual("My Extension Manifest", first.Name); + Assert.AreEqual("My Plugin Configuration", first.Name); Assert.AreEqual("1.2.3", first.Version); Assert.AreEqual(2, first.Extensions.Count()); Assert.IsTrue(first.Extensions.All(e => e is JsonElement)); } [Test] - public async Task CanReadManifestsInRootDirectories() + public async Task Can_Read_PluginConfiguration_In_Root_Directories() { - var directory1 = CreateDirectoryMock("/my-extension", CreateExtensionManifestFile(DefaultManifestContent("Extension One"))); - var directory2 = CreateDirectoryMock("/my-other-extension", CreateExtensionManifestFile(DefaultManifestContent("Extension Two"))); + var plugin1 = CreateDirectoryMock("/my-extension", CreatePluginConfigurationFile(DefaultPluginConfigurationContent("Plugin One"))); + var plugin2 = CreateDirectoryMock("/my-other-extension", CreatePluginConfigurationFile(DefaultPluginConfigurationContent("Plugin Two"))); _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) - .Returns(new List { directory1, directory2 }.GetEnumerator()); + .Returns(new List { plugin1, plugin2 }.GetEnumerator()); - var result = await _reader.ReadManifestsAsync(); + var result = await _reader.ReadPluginConfigurationsAsync(); Assert.AreEqual(2, result.Count()); - Assert.AreEqual("Extension One", result.First().Name); - Assert.AreEqual("Extension Two", result.Last().Name); + Assert.AreEqual("Plugin One", result.First().Name); + Assert.AreEqual("Plugin Two", result.Last().Name); } [Test] - public async Task CanReadManifestsRecursively() + public async Task Can_Read_PluginConfigurations_Recursively() { - var childFolder = CreateDirectoryMock("/my-parent-folder/my-child-folder", CreateExtensionManifestFile(DefaultManifestContent("Nested Extension"))); + var childFolder = CreateDirectoryMock("/my-parent-folder/my-child-folder", CreatePluginConfigurationFile(DefaultPluginConfigurationContent("Nested Plugin"))); var parentFolder = CreateDirectoryMock("/my-parent-folder", childFolder); _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) .Returns(new List { parentFolder }.GetEnumerator()); - var result = await _reader.ReadManifestsAsync(); + var result = await _reader.ReadPluginConfigurationsAsync(); Assert.AreEqual(1, result.Count()); - Assert.AreEqual("Nested Extension", result.First().Name); + Assert.AreEqual("Nested Plugin", result.First().Name); } [Test] - public async Task CanSkipEmptyDirectories() + public async Task Can_Skip_Empty_Directories() { - var extensionFolder = CreateDirectoryMock("/my-extension-folder", CreateExtensionManifestFile(DefaultManifestContent("My Extension"))); + var pluginFolder = CreateDirectoryMock("/my-plugin-folder", CreatePluginConfigurationFile(DefaultPluginConfigurationContent("My Plugin"))); var emptyFolder = CreateDirectoryMock("/my-empty-folder"); _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) - .Returns(new List { emptyFolder, extensionFolder }.GetEnumerator()); + .Returns(new List { emptyFolder, pluginFolder }.GetEnumerator()); - var result = await _reader.ReadManifestsAsync(); + var result = await _reader.ReadPluginConfigurationsAsync(); Assert.AreEqual(1, result.Count()); - Assert.AreEqual("My Extension", result.First().Name); + Assert.AreEqual("My Plugin", result.First().Name); } [Test] - public async Task CanSkipOtherFiles() + public async Task Can_Skip_Other_Files() { - var extensionFolder = CreateDirectoryMock( - "/my-extension-folder", + var pluginFolder = CreateDirectoryMock( + "/my-plugin-folder", CreateOtherFile("my.js"), - CreateExtensionManifestFile(DefaultManifestContent("My Extension"))); + CreatePluginConfigurationFile(DefaultPluginConfigurationContent("My Plugin"))); var otherFolder = CreateDirectoryMock( "/my-empty-folder", CreateOtherFile("some.js"), @@ -110,15 +110,15 @@ public class ExtensionManifestReaderTests _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) - .Returns(new List { otherFolder, extensionFolder }.GetEnumerator()); + .Returns(new List { otherFolder, pluginFolder }.GetEnumerator()); - var result = await _reader.ReadManifestsAsync(); + var result = await _reader.ReadPluginConfigurationsAsync(); Assert.AreEqual(1, result.Count()); - Assert.AreEqual("My Extension", result.First().Name); + Assert.AreEqual("My Plugin", result.First().Name); } [Test] - public async Task CanHandleAllEmptyDirectories() + public async Task Can_Handle_All_Empty_Directories() { var folders = Enumerable.Range(1, 10).Select(i => CreateDirectoryMock($"/my-empty-folder-{i}")).ToList(); @@ -126,12 +126,12 @@ public class ExtensionManifestReaderTests .Setup(f => f.GetEnumerator()) .Returns(folders.GetEnumerator()); - var result = await _reader.ReadManifestsAsync(); + var result = await _reader.ReadPluginConfigurationsAsync(); Assert.AreEqual(0, result.Count()); } [Test] - public async Task CannotReadManifestWithoutName() + public async Task Cannot_Read_PluginConfiguration_Without_Name() { var content = @"{ ""version"": ""1.2.3"", @@ -145,16 +145,16 @@ public class ExtensionManifestReaderTests }"; _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) - .Returns(new List { CreateExtensionManifestFile(content) }.GetEnumerator()); + .Returns(new List { CreatePluginConfigurationFile(content) }.GetEnumerator()); - var result = await _reader.ReadManifestsAsync(); + var result = await _reader.ReadPluginConfigurationsAsync(); Assert.AreEqual(0, result.Count()); EnsureLogErrorWasCalled(); } [Test] - public async Task CannotReadManifestWithoutExtensions() + public async Task Cannot_Read_PluginConfiguration_Without_Extensions() { var content = @"{ ""name"": ""Something"", @@ -163,9 +163,9 @@ public class ExtensionManifestReaderTests }"; _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) - .Returns(new List { CreateExtensionManifestFile(content) }.GetEnumerator()); + .Returns(new List { CreatePluginConfigurationFile(content) }.GetEnumerator()); - var result = await _reader.ReadManifestsAsync(); + var result = await _reader.ReadPluginConfigurationsAsync(); Assert.AreEqual(0, result.Count()); EnsureLogErrorWasCalled(); @@ -173,13 +173,13 @@ public class ExtensionManifestReaderTests [TestCase("This is not JSON")] [TestCase(@"{""name"": ""invalid-json"", ""version"": ")] - public async Task CannotReadInvalidManifest(string content) + public async Task Cannot_Read_Invalid_PluginConfiguration(string content) { _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) - .Returns(new List { CreateExtensionManifestFile(content) }.GetEnumerator()); + .Returns(new List { CreatePluginConfigurationFile(content) }.GetEnumerator()); - var result = await _reader.ReadManifestsAsync(); + var result = await _reader.ReadPluginConfigurationsAsync(); Assert.AreEqual(0, result.Count()); EnsureLogErrorWasCalled(); @@ -213,9 +213,9 @@ public class ExtensionManifestReaderTests return fileInfo.Object; } - private IFileInfo CreateExtensionManifestFile(string? content = null) + private IFileInfo CreatePluginConfigurationFile(string? content = null) { - content ??= DefaultManifestContent(); + content ??= DefaultPluginConfigurationContent(); var fileInfo = new Mock(); fileInfo.SetupGet(f => f.IsDirectory).Returns(false); @@ -235,7 +235,7 @@ public class ExtensionManifestReaderTests return fileInfo.Object; } - private static string DefaultManifestContent(string name = "My Extension Manifest") + private static string DefaultPluginConfigurationContent(string name = "My Plugin Configuration") => @"{ ""name"": ""##NAME##"", ""version"": ""1.2.3"", diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Plugin/PluginConfigurationServiceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Plugin/PluginConfigurationServiceTests.cs new file mode 100644 index 0000000000..016799f58a --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Plugin/PluginConfigurationServiceTests.cs @@ -0,0 +1,67 @@ +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Plugin; +using Umbraco.Cms.Infrastructure.Plugin; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Plugin; + +[TestFixture] +public class PluginConfigurationServiceTests +{ + private IPluginConfigurationService _service; + private Mock _readerMock; + private IAppPolicyCache _runtimeCache; + + [SetUp] + public void SetUp() + { + _readerMock = new Mock(); + _readerMock.Setup(r => r.ReadPluginConfigurationsAsync()).ReturnsAsync( + new[] + { + new PluginConfiguration { Name = "Test", Extensions = Array.Empty() } + }); + + _runtimeCache = new ObjectCacheAppCache(); + AppCaches appCaches = new AppCaches( + _runtimeCache, + NoAppCache.Instance, + new IsolatedCaches(type => NoAppCache.Instance)); + + _service = new PluginConfigurationService(_readerMock.Object, appCaches); + } + + [Test] + public async Task CachesExtensionPackageConfigurations() + { + var result = await _service.GetPluginConfigurationsAsync(); + Assert.AreEqual(1, result.Count()); + + var result2 = await _service.GetPluginConfigurationsAsync(); + Assert.AreEqual(1, result2.Count()); + + var result3 = await _service.GetPluginConfigurationsAsync(); + Assert.AreEqual(1, result3.Count()); + + _readerMock.Verify(r => r.ReadPluginConfigurationsAsync(), Times.Exactly(1)); + } + + [Test] + public async Task ReloadsExtensionPackageConfigurationsAfterCacheClear() + { + var result = await _service.GetPluginConfigurationsAsync(); + Assert.AreEqual(1, result.Count()); + _runtimeCache.Clear(); + + var result2 = await _service.GetPluginConfigurationsAsync(); + Assert.AreEqual(1, result2.Count()); + _runtimeCache.Clear(); + + var result3 = await _service.GetPluginConfigurationsAsync(); + Assert.AreEqual(1, result3.Count()); + _runtimeCache.Clear(); + + _readerMock.Verify(r => r.ReadPluginConfigurationsAsync(), Times.Exactly(3)); + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs index 88bcbb710c..f9a69a6aa1 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs @@ -1,6 +1,7 @@ using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration; +using Umbraco.Cms.Core.Plugin; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Semver; @@ -23,7 +24,7 @@ public class TelemetryServiceTests siteIdentifierServiceMock.Object, usageInformationServiceMock.Object, Mock.Of(), - Mock.Of()); + Mock.Of()); Guid guid; await sut.GetTelemetryReportDataAsync(); @@ -39,7 +40,7 @@ public class TelemetryServiceTests CreateSiteIdentifierService(false), Mock.Of(), Mock.Of(), - Mock.Of()); + Mock.Of()); var result = await sut.GetTelemetryReportDataAsync(); Assert.IsNull(result); @@ -57,7 +58,7 @@ public class TelemetryServiceTests CreateSiteIdentifierService(), Mock.Of(), metricsConsentService.Object, - Mock.Of()); + Mock.Of()); var result = await sut.GetTelemetryReportDataAsync(); @@ -72,7 +73,7 @@ public class TelemetryServiceTests var versionPackageName = "VersionPackage"; var packageVersion = "1.0.0"; var noVersionPackageName = "NoVersionPackage"; - ExtensionManifest[] manifests = + PluginConfiguration[] manifests = { new() { Name = versionPackageName, Version = packageVersion, Extensions = Array.Empty()}, new() { Name = noVersionPackageName, Extensions = Array.Empty() }, @@ -107,7 +108,7 @@ public class TelemetryServiceTests public async Task RespectsAllowPackageTelemetry() { var version = CreateUmbracoVersion(9, 1, 1); - ExtensionManifest[] manifests = + PluginConfiguration[] manifests = { new() { Name = "DoNotTrack", AllowTelemetry = false, Extensions = Array.Empty() }, new() { Name = "TrackingAllowed", AllowTelemetry = true, Extensions = Array.Empty() }, @@ -132,10 +133,10 @@ public class TelemetryServiceTests }); } - private IExtensionManifestService CreateExtensionManifestService(IEnumerable manifests) + private IPluginConfigurationService CreateExtensionManifestService(IEnumerable manifests) { - var mock = new Mock(); - mock.Setup(x => x.GetManifestsAsync()).Returns(Task.FromResult(manifests)); + var mock = new Mock(); + mock.Setup(x => x.GetPluginConfigurationsAsync()).Returns(Task.FromResult(manifests)); return mock.Object; } From e4223ef2deb14d69c33d87dcb8fc496e8ab97006 Mon Sep 17 00:00:00 2001 From: kjac Date: Tue, 14 Feb 2023 19:08:21 +0100 Subject: [PATCH 09/20] Use async telemetry report in ReportSiteTask --- .../HostedServices/ReportSiteTask.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs b/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs index 95997dbbf5..47f1afe650 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs @@ -40,18 +40,20 @@ public class ReportSiteTask : RecurringHostedServiceBase { } - /// - /// Runs the background task to send the anonymous ID - /// to telemetry service - /// - public override async Task PerformExecuteAsync(object? state){ + /// + /// Runs the background task to send the anonymous ID + /// to telemetry service + /// + public override async Task PerformExecuteAsync(object? state) + { if (_runtimeState.Level is not RuntimeLevel.Run) { // We probably haven't installed yet, so we can't get telemetry. return; } - if (_telemetryService.TryGetTelemetryReportData(out TelemetryReportData? telemetryReportData) is false) + TelemetryReportData? telemetryReportData = await _telemetryService.GetTelemetryReportDataAsync(); + if (telemetryReportData is null) { _logger.LogWarning("No telemetry marker found"); From d2ed03a5d81e39fdd26d79e17ee8f15aedf78584 Mon Sep 17 00:00:00 2001 From: kjac Date: Wed, 15 Feb 2023 14:57:21 +0100 Subject: [PATCH 10/20] Rename old manifest stuff to legacy manifest --- .../CompatibilitySuppressions.xml | 112 ++++++++++++++++++ .../Configuration/Grid/GridConfig.cs | 8 +- .../Configuration/Grid/GridEditorsConfig.cs | 12 +- .../ContentAppFactoryCollectionBuilder.cs | 6 +- .../Dashboards/DashboardCollectionBuilder.cs | 8 +- .../UmbracoBuilder.CollectionBuilders.cs | 2 +- .../UmbracoBuilder.Collections.cs | 6 +- ...t.cs => CompositeLegacyPackageManifest.cs} | 24 ++-- ...festFilter.cs => ILegacyManifestFilter.cs} | 4 +- ...festParser.cs => ILegacyManifestParser.cs} | 8 +- ...eManifest.cs => ILegacyPackageManifest.cs} | 8 +- ...ifestAssets.cs => LegacyManifestAssets.cs} | 4 +- ... => LegacyManifestContentAppDefinition.cs} | 4 +- ....cs => LegacyManifestContentAppFactory.cs} | 6 +- ...ashboard.cs => LegacyManifestDashboard.cs} | 2 +- ...n.cs => LegacyManifestFilterCollection.cs} | 8 +- ... LegacyManifestFilterCollectionBuilder.cs} | 6 +- ...estSection.cs => LegacyManifestSection.cs} | 2 +- ...geManifest.cs => LegacyPackageManifest.cs} | 8 +- .../Models/Mapping/SectionMapDefinition.cs | 4 +- .../ParameterEditorCollection.cs | 4 +- .../PropertyEditorCollection.cs | 4 +- .../Sections/SectionCollectionBuilder.cs | 4 +- .../Telemetry/TelemetryService.cs | 6 +- .../CompatibilitySuppressions.xml | 28 +++++ .../UmbracoBuilder.CoreServices.cs | 2 +- ...ifestParser.cs => LegacyManifestParser.cs} | 70 +++++------ .../Services/Implement/PackagingService.cs | 8 +- .../WebAssets/BackOfficeWebAssets.cs | 22 ++-- .../CompatibilitySuppressions.xml | 17 +++ .../Controllers/BackOfficeController.cs | 8 +- .../Extensions/RuntimeMinifierExtensions.cs | 18 +-- ...ts.cs => LegacyManifestContentAppTests.cs} | 6 +- ...rTests.cs => LegacyManifestParserTests.cs} | 18 +-- 34 files changed, 307 insertions(+), 150 deletions(-) rename src/Umbraco.Core/Manifest/{CompositePackageManifest.cs => CompositeLegacyPackageManifest.cs} (68%) rename src/Umbraco.Core/Manifest/{IManifestFilter.cs => ILegacyManifestFilter.cs} (79%) rename src/Umbraco.Core/Manifest/{IManifestParser.cs => ILegacyManifestParser.cs} (66%) rename src/Umbraco.Core/Manifest/{IPackageManifest.cs => ILegacyPackageManifest.cs} (89%) rename src/Umbraco.Core/Manifest/{ManifestAssets.cs => LegacyManifestAssets.cs} (72%) rename src/Umbraco.Core/Manifest/{ManifestContentAppDefinition.cs => LegacyManifestContentAppDefinition.cs} (93%) rename src/Umbraco.Core/Manifest/{ManifestContentAppFactory.cs => LegacyManifestContentAppFactory.cs} (96%) rename src/Umbraco.Core/Manifest/{ManifestDashboard.cs => LegacyManifestDashboard.cs} (92%) rename src/Umbraco.Core/Manifest/{ManifestFilterCollection.cs => LegacyManifestFilterCollection.cs} (56%) rename src/Umbraco.Core/Manifest/{ManifestFilterCollectionBuilder.cs => LegacyManifestFilterCollectionBuilder.cs} (50%) rename src/Umbraco.Core/Manifest/{ManifestSection.cs => LegacyManifestSection.cs} (87%) rename src/Umbraco.Core/Manifest/{PackageManifest.cs => LegacyPackageManifest.cs} (90%) rename src/Umbraco.Infrastructure/Manifest/{ManifestParser.cs => LegacyManifestParser.cs} (81%) create mode 100644 src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml rename tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/{ManifestContentAppTests.cs => LegacyManifestContentAppTests.cs} (94%) rename tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/{ManifestParserTests.cs => LegacyManifestParserTests.cs} (96%) diff --git a/src/Umbraco.Core/CompatibilitySuppressions.xml b/src/Umbraco.Core/CompatibilitySuppressions.xml index ad50b77190..6170e7c8d1 100644 --- a/src/Umbraco.Core/CompatibilitySuppressions.xml +++ b/src/Umbraco.Core/CompatibilitySuppressions.xml @@ -1,5 +1,96 @@  + + CP0001 + T:Umbraco.Cms.Core.Manifest.CompositePackageManifest + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.IManifestFilter + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.IManifestParser + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.IPackageManifest + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestAssets + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestContentAppDefinition + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestContentAppFactory + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestDashboard + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestFilterCollection + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestFilterCollectionBuilder + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestSection + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.PackageManifest + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Configuration.Grid.GridConfig.#ctor(Umbraco.Cms.Core.Cache.AppCaches,Umbraco.Cms.Core.Manifest.IManifestParser,Umbraco.Cms.Core.Serialization.IJsonSerializer,Umbraco.Cms.Core.Hosting.IHostingEnvironment,Microsoft.Extensions.Logging.ILoggerFactory) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + CP0002 M:Umbraco.Cms.Core.Models.Blocks.BlockGridItem.get_ForceLeft @@ -343,6 +434,20 @@ lib/net7.0/Umbraco.Core.dll true + + CP0002 + M:Umbraco.Cms.Core.PropertyEditors.ParameterEditorCollection.#ctor(Umbraco.Cms.Core.PropertyEditors.DataEditorCollection,Umbraco.Cms.Core.Manifest.IManifestParser) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.PropertyEditors.PropertyEditorCollection.#ctor(Umbraco.Cms.Core.PropertyEditors.DataEditorCollection,Umbraco.Cms.Core.Manifest.IManifestParser) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + CP0002 M:Umbraco.Cms.Core.Services.Implement.DataTypeService.#ctor(Umbraco.Cms.Core.PropertyEditors.IDataValueEditorFactory,Umbraco.Cms.Core.Scoping.ICoreScopeProvider,Microsoft.Extensions.Logging.ILoggerFactory,Umbraco.Cms.Core.Events.IEventMessagesFactory,Umbraco.Cms.Core.Persistence.Repositories.IDataTypeRepository,Umbraco.Cms.Core.Persistence.Repositories.IDataTypeContainerRepository,Umbraco.Cms.Core.Persistence.Repositories.IAuditRepository,Umbraco.Cms.Core.Persistence.Repositories.IEntityRepository,Umbraco.Cms.Core.Persistence.Repositories.IContentTypeRepository,Umbraco.Cms.Core.IO.IIOHelper,Umbraco.Cms.Core.Services.ILocalizedTextService,Umbraco.Cms.Core.Services.ILocalizationService,Umbraco.Cms.Core.Strings.IShortStringHelper,Umbraco.Cms.Core.Serialization.IJsonSerializer) @@ -567,6 +672,13 @@ lib/net7.0/Umbraco.Core.dll true + + CP0006 + M:Umbraco.Cms.Core.Telemetry.ITelemetryService.GetTelemetryReportDataAsync + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + CP0006 P:Umbraco.Cms.Core.Models.IDataType.ConfigurationData diff --git a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs index be13776da8..a8ef00e9c0 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs @@ -13,24 +13,24 @@ public class GridConfig : IGridConfig { public GridConfig( AppCaches appCaches, - IManifestParser manifestParser, + ILegacyManifestParser legacyManifestParser, IJsonSerializer jsonSerializer, IHostingEnvironment hostingEnvironment, ILoggerFactory loggerFactory, IGridEditorsConfigFileProviderFactory gridEditorsConfigFileProviderFactory) => EditorsConfig = - new GridEditorsConfig(appCaches, hostingEnvironment, manifestParser, jsonSerializer, loggerFactory.CreateLogger(), gridEditorsConfigFileProviderFactory); + new GridEditorsConfig(appCaches, hostingEnvironment, legacyManifestParser, jsonSerializer, loggerFactory.CreateLogger(), gridEditorsConfigFileProviderFactory); [Obsolete("Use other ctor - Will be removed in Umbraco 13")] public GridConfig( AppCaches appCaches, - IManifestParser manifestParser, + ILegacyManifestParser legacyManifestParser, IJsonSerializer jsonSerializer, IHostingEnvironment hostingEnvironment, ILoggerFactory loggerFactory) : this( appCaches, - manifestParser, + legacyManifestParser, jsonSerializer, hostingEnvironment, loggerFactory, diff --git a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs index ab0ec8b182..64d471faf9 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs @@ -21,19 +21,19 @@ internal class GridEditorsConfig : IGridEditorsConfig private readonly IJsonSerializer _jsonSerializer; private readonly ILogger _logger; private readonly IGridEditorsConfigFileProviderFactory _gridEditorsConfigFileProviderFactory; - private readonly IManifestParser _manifestParser; + private readonly ILegacyManifestParser _legacyManifestParser; public GridEditorsConfig( AppCaches appCaches, IHostingEnvironment hostingEnvironment, - IManifestParser manifestParser, + ILegacyManifestParser legacyManifestParser, IJsonSerializer jsonSerializer, ILogger logger, IGridEditorsConfigFileProviderFactory gridEditorsConfigFileProviderFactory) { _appCaches = appCaches; _hostingEnvironment = hostingEnvironment; - _manifestParser = manifestParser; + _legacyManifestParser = legacyManifestParser; _jsonSerializer = jsonSerializer; _logger = logger; _gridEditorsConfigFileProviderFactory = gridEditorsConfigFileProviderFactory; @@ -43,13 +43,13 @@ internal class GridEditorsConfig : IGridEditorsConfig public GridEditorsConfig( AppCaches appCaches, IHostingEnvironment hostingEnvironment, - IManifestParser manifestParser, + ILegacyManifestParser legacyManifestParser, IJsonSerializer jsonSerializer, ILogger logger) : this( appCaches, hostingEnvironment, - manifestParser, + legacyManifestParser, jsonSerializer, logger, StaticServiceProvider.Instance.GetRequiredService()) @@ -120,7 +120,7 @@ internal class GridEditorsConfig : IGridEditorsConfig } // Add manifest editors, skip duplicates - foreach (GridEditor gridEditor in _manifestParser.CombinedManifest.GridEditors) + foreach (GridEditor gridEditor in _legacyManifestParser.CombinedManifest.GridEditors) { if (editors.Contains(gridEditor) == false) { diff --git a/src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs b/src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs index fe6fdd423a..002fe15628 100644 --- a/src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs +++ b/src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs @@ -27,10 +27,10 @@ public class ContentAppFactoryCollectionBuilder : OrderedCollectionBuilderBase(); + ILegacyManifestParser legacyManifestParser = factory.GetRequiredService(); IIOHelper ioHelper = factory.GetRequiredService(); return base.CreateItems(factory) - .Concat(manifestParser.CombinedManifest.ContentApps.Select(x => - new ManifestContentAppFactory(x, ioHelper))); + .Concat(legacyManifestParser.CombinedManifest.ContentApps.Select(x => + new LegacyManifestContentAppFactory(x, ioHelper))); } } diff --git a/src/Umbraco.Core/Dashboards/DashboardCollectionBuilder.cs b/src/Umbraco.Core/Dashboards/DashboardCollectionBuilder.cs index 50867c90f4..3e3fed260f 100644 --- a/src/Umbraco.Core/Dashboards/DashboardCollectionBuilder.cs +++ b/src/Umbraco.Core/Dashboards/DashboardCollectionBuilder.cs @@ -13,17 +13,17 @@ public class DashboardCollectionBuilder : WeightedCollectionBuilderBase(); + ILegacyManifestParser legacyManifestParser = factory.GetRequiredService(); IEnumerable dashboardSections = - Merge(base.CreateItems(factory), manifestParser.CombinedManifest.Dashboards); + Merge(base.CreateItems(factory), legacyManifestParser.CombinedManifest.Dashboards); return dashboardSections; } private IEnumerable Merge( IEnumerable dashboardsFromCode, - IReadOnlyList dashboardFromManifest) => + IReadOnlyList dashboardFromManifest) => dashboardsFromCode.Concat(dashboardFromManifest) .Where(x => !string.IsNullOrEmpty(x.Alias)) .OrderBy(GetWeight); @@ -32,7 +32,7 @@ public class DashboardCollectionBuilder : WeightedCollectionBuilderBaseThe type of the manifest filter. /// The Builder. public static IUmbracoBuilder AddManifestFilter(this IUmbracoBuilder builder) - where T : class, IManifestFilter + where T : class, ILegacyManifestFilter { builder.ManifestFilters().Append(); return builder; diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs index 6f735a003c..93ac06f4f8 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs @@ -83,7 +83,7 @@ public static partial class UmbracoBuilderExtensions .Add() .Add() .Add() - .Add() + .Add() .Add() .Add() .Add() @@ -270,8 +270,8 @@ public static partial class UmbracoBuilderExtensions /// Gets the manifest filter collection builder. /// /// The builder. - public static ManifestFilterCollectionBuilder ManifestFilters(this IUmbracoBuilder builder) - => builder.WithCollectionBuilder(); + public static LegacyManifestFilterCollectionBuilder ManifestFilters(this IUmbracoBuilder builder) + => builder.WithCollectionBuilder(); /// /// Gets the content finders collection builder. diff --git a/src/Umbraco.Core/Manifest/CompositePackageManifest.cs b/src/Umbraco.Core/Manifest/CompositeLegacyPackageManifest.cs similarity index 68% rename from src/Umbraco.Core/Manifest/CompositePackageManifest.cs rename to src/Umbraco.Core/Manifest/CompositeLegacyPackageManifest.cs index 5e41681ea6..d0512a1753 100644 --- a/src/Umbraco.Core/Manifest/CompositePackageManifest.cs +++ b/src/Umbraco.Core/Manifest/CompositeLegacyPackageManifest.cs @@ -5,17 +5,17 @@ namespace Umbraco.Cms.Core.Manifest; /// /// A package manifest made up of all combined manifests /// -public class CompositePackageManifest +public class CompositeLegacyPackageManifest { - public CompositePackageManifest( + public CompositeLegacyPackageManifest( IReadOnlyList propertyEditors, IReadOnlyList parameterEditors, IReadOnlyList gridEditors, - IReadOnlyList contentApps, - IReadOnlyList dashboards, - IReadOnlyList sections, - IReadOnlyDictionary> scripts, - IReadOnlyDictionary> stylesheets) + IReadOnlyList contentApps, + IReadOnlyList dashboards, + IReadOnlyList sections, + IReadOnlyDictionary> scripts, + IReadOnlyDictionary> stylesheets) { PropertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors)); ParameterEditors = parameterEditors ?? throw new ArgumentNullException(nameof(parameterEditors)); @@ -45,19 +45,19 @@ public class CompositePackageManifest /// /// Gets or sets the content apps listed in the manifest. /// - public IReadOnlyList ContentApps { get; } + public IReadOnlyList ContentApps { get; } /// /// Gets or sets the dashboards listed in the manifest. /// - public IReadOnlyList Dashboards { get; } + public IReadOnlyList Dashboards { get; } /// /// Gets or sets the sections listed in the manifest. /// - public IReadOnlyCollection Sections { get; } + public IReadOnlyCollection Sections { get; } - public IReadOnlyDictionary> Scripts { get; } + public IReadOnlyDictionary> Scripts { get; } - public IReadOnlyDictionary> Stylesheets { get; } + public IReadOnlyDictionary> Stylesheets { get; } } diff --git a/src/Umbraco.Core/Manifest/IManifestFilter.cs b/src/Umbraco.Core/Manifest/ILegacyManifestFilter.cs similarity index 79% rename from src/Umbraco.Core/Manifest/IManifestFilter.cs rename to src/Umbraco.Core/Manifest/ILegacyManifestFilter.cs index d2998a0839..fd72466cbc 100644 --- a/src/Umbraco.Core/Manifest/IManifestFilter.cs +++ b/src/Umbraco.Core/Manifest/ILegacyManifestFilter.cs @@ -3,7 +3,7 @@ namespace Umbraco.Cms.Core.Manifest; /// /// Provides filtering for package manifests. /// -public interface IManifestFilter +public interface ILegacyManifestFilter { /// /// Filters package manifests. @@ -12,5 +12,5 @@ public interface IManifestFilter /// /// It is possible to remove, change, or add manifests. /// - void Filter(List manifests); + void Filter(List manifests); } diff --git a/src/Umbraco.Core/Manifest/IManifestParser.cs b/src/Umbraco.Core/Manifest/ILegacyManifestParser.cs similarity index 66% rename from src/Umbraco.Core/Manifest/IManifestParser.cs rename to src/Umbraco.Core/Manifest/ILegacyManifestParser.cs index f8b29e9f56..0a854774b3 100644 --- a/src/Umbraco.Core/Manifest/IManifestParser.cs +++ b/src/Umbraco.Core/Manifest/ILegacyManifestParser.cs @@ -1,6 +1,6 @@ namespace Umbraco.Cms.Core.Manifest; -public interface IManifestParser +public interface ILegacyManifestParser { string AppPluginsPath { get; set; } @@ -8,16 +8,16 @@ public interface IManifestParser /// Gets all manifests, merged into a single manifest object. /// /// - CompositePackageManifest CombinedManifest { get; } + CompositeLegacyPackageManifest CombinedManifest { get; } /// /// Parses a manifest. /// - PackageManifest ParseManifest(string text); + LegacyPackageManifest ParseManifest(string text); /// /// Returns all package individual manifests /// /// - IEnumerable GetManifests(); + IEnumerable GetManifests(); } diff --git a/src/Umbraco.Core/Manifest/IPackageManifest.cs b/src/Umbraco.Core/Manifest/ILegacyPackageManifest.cs similarity index 89% rename from src/Umbraco.Core/Manifest/IPackageManifest.cs rename to src/Umbraco.Core/Manifest/ILegacyPackageManifest.cs index ba911b183c..21e1903e18 100644 --- a/src/Umbraco.Core/Manifest/IPackageManifest.cs +++ b/src/Umbraco.Core/Manifest/ILegacyPackageManifest.cs @@ -3,7 +3,7 @@ using Umbraco.Cms.Core.PropertyEditors; namespace Umbraco.Cms.Core.Manifest; -public interface IPackageManifest +public interface ILegacyPackageManifest { /// /// Gets the source path of the manifest. @@ -50,17 +50,17 @@ public interface IPackageManifest /// Gets or sets the content apps listed in the manifest. /// [DataMember(Name = "contentApps")] - ManifestContentAppDefinition[] ContentApps { get; set; } + LegacyManifestContentAppDefinition[] ContentApps { get; set; } /// /// Gets or sets the dashboards listed in the manifest. /// [DataMember(Name = "dashboards")] - ManifestDashboard[] Dashboards { get; set; } + LegacyManifestDashboard[] Dashboards { get; set; } /// /// Gets or sets the sections listed in the manifest. /// [DataMember(Name = "sections")] - ManifestSection[] Sections { get; set; } + LegacyManifestSection[] Sections { get; set; } } diff --git a/src/Umbraco.Core/Manifest/ManifestAssets.cs b/src/Umbraco.Core/Manifest/LegacyManifestAssets.cs similarity index 72% rename from src/Umbraco.Core/Manifest/ManifestAssets.cs rename to src/Umbraco.Core/Manifest/LegacyManifestAssets.cs index 2bd84a1bdd..05b0123a0d 100644 --- a/src/Umbraco.Core/Manifest/ManifestAssets.cs +++ b/src/Umbraco.Core/Manifest/LegacyManifestAssets.cs @@ -1,8 +1,8 @@ namespace Umbraco.Cms.Core.Manifest; -public class ManifestAssets +public class LegacyManifestAssets { - public ManifestAssets(string? packageName, IReadOnlyList assets) + public LegacyManifestAssets(string? packageName, IReadOnlyList assets) { PackageName = packageName ?? throw new ArgumentNullException(nameof(packageName)); Assets = assets ?? throw new ArgumentNullException(nameof(assets)); diff --git a/src/Umbraco.Core/Manifest/ManifestContentAppDefinition.cs b/src/Umbraco.Core/Manifest/LegacyManifestContentAppDefinition.cs similarity index 93% rename from src/Umbraco.Core/Manifest/ManifestContentAppDefinition.cs rename to src/Umbraco.Core/Manifest/LegacyManifestContentAppDefinition.cs index 5bfc2a740e..914923ec31 100644 --- a/src/Umbraco.Core/Manifest/ManifestContentAppDefinition.cs +++ b/src/Umbraco.Core/Manifest/LegacyManifestContentAppDefinition.cs @@ -22,9 +22,9 @@ namespace Umbraco.Cms.Core.Manifest; /// /// Represents a content app definition, parsed from a manifest. /// -/// Is used to create an actual . +/// Is used to create an actual . [DataContract(Name = "appdef", Namespace = "")] -public class ManifestContentAppDefinition +public class LegacyManifestContentAppDefinition { private readonly string? _view; diff --git a/src/Umbraco.Core/Manifest/ManifestContentAppFactory.cs b/src/Umbraco.Core/Manifest/LegacyManifestContentAppFactory.cs similarity index 96% rename from src/Umbraco.Core/Manifest/ManifestContentAppFactory.cs rename to src/Umbraco.Core/Manifest/LegacyManifestContentAppFactory.cs index 122ecc1cb7..56795f7e59 100644 --- a/src/Umbraco.Core/Manifest/ManifestContentAppFactory.cs +++ b/src/Umbraco.Core/Manifest/LegacyManifestContentAppFactory.cs @@ -29,15 +29,15 @@ namespace Umbraco.Cms.Core.Manifest; /// /// Represents a content app factory, for content apps parsed from the manifest. /// -public class ManifestContentAppFactory : IContentAppFactory +public class LegacyManifestContentAppFactory : IContentAppFactory { - private readonly ManifestContentAppDefinition _definition; + private readonly LegacyManifestContentAppDefinition _definition; private readonly IIOHelper _ioHelper; private ContentApp? _app; private ShowRule[]? _showRules; - public ManifestContentAppFactory(ManifestContentAppDefinition definition, IIOHelper ioHelper) + public LegacyManifestContentAppFactory(LegacyManifestContentAppDefinition definition, IIOHelper ioHelper) { _definition = definition; _ioHelper = ioHelper; diff --git a/src/Umbraco.Core/Manifest/ManifestDashboard.cs b/src/Umbraco.Core/Manifest/LegacyManifestDashboard.cs similarity index 92% rename from src/Umbraco.Core/Manifest/ManifestDashboard.cs rename to src/Umbraco.Core/Manifest/LegacyManifestDashboard.cs index 75cdf24ebe..9cc0ad9091 100644 --- a/src/Umbraco.Core/Manifest/ManifestDashboard.cs +++ b/src/Umbraco.Core/Manifest/LegacyManifestDashboard.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Dashboards; namespace Umbraco.Cms.Core.Manifest; [DataContract] -public class ManifestDashboard : IDashboard +public class LegacyManifestDashboard : IDashboard { [DataMember(Name = "weight")] public int Weight { get; set; } = 100; diff --git a/src/Umbraco.Core/Manifest/ManifestFilterCollection.cs b/src/Umbraco.Core/Manifest/LegacyManifestFilterCollection.cs similarity index 56% rename from src/Umbraco.Core/Manifest/ManifestFilterCollection.cs rename to src/Umbraco.Core/Manifest/LegacyManifestFilterCollection.cs index a1d5cac0c1..a2af5ee440 100644 --- a/src/Umbraco.Core/Manifest/ManifestFilterCollection.cs +++ b/src/Umbraco.Core/Manifest/LegacyManifestFilterCollection.cs @@ -5,9 +5,9 @@ namespace Umbraco.Cms.Core.Manifest; /// /// Contains the manifest filters. /// -public class ManifestFilterCollection : BuilderCollectionBase +public class LegacyManifestFilterCollection : BuilderCollectionBase { - public ManifestFilterCollection(Func> items) + public LegacyManifestFilterCollection(Func> items) : base(items) { } @@ -16,9 +16,9 @@ public class ManifestFilterCollection : BuilderCollectionBase /// Filters package manifests. /// /// The package manifests. - public void Filter(List manifests) + public void Filter(List manifests) { - foreach (IManifestFilter filter in this) + foreach (ILegacyManifestFilter filter in this) { filter.Filter(manifests); } diff --git a/src/Umbraco.Core/Manifest/ManifestFilterCollectionBuilder.cs b/src/Umbraco.Core/Manifest/LegacyManifestFilterCollectionBuilder.cs similarity index 50% rename from src/Umbraco.Core/Manifest/ManifestFilterCollectionBuilder.cs rename to src/Umbraco.Core/Manifest/LegacyManifestFilterCollectionBuilder.cs index 5f012d10c9..ae8c41a9f0 100644 --- a/src/Umbraco.Core/Manifest/ManifestFilterCollectionBuilder.cs +++ b/src/Umbraco.Core/Manifest/LegacyManifestFilterCollectionBuilder.cs @@ -3,10 +3,10 @@ using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Manifest; -public class ManifestFilterCollectionBuilder : OrderedCollectionBuilderBase +public class LegacyManifestFilterCollectionBuilder : OrderedCollectionBuilderBase { - protected override ManifestFilterCollectionBuilder This => this; + protected override LegacyManifestFilterCollectionBuilder This => this; // do NOT cache this, it's only used once protected override ServiceLifetime CollectionLifetime => ServiceLifetime.Transient; diff --git a/src/Umbraco.Core/Manifest/ManifestSection.cs b/src/Umbraco.Core/Manifest/LegacyManifestSection.cs similarity index 87% rename from src/Umbraco.Core/Manifest/ManifestSection.cs rename to src/Umbraco.Core/Manifest/LegacyManifestSection.cs index c7671c91e2..e0b0446523 100644 --- a/src/Umbraco.Core/Manifest/ManifestSection.cs +++ b/src/Umbraco.Core/Manifest/LegacyManifestSection.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Sections; namespace Umbraco.Cms.Core.Manifest; [DataContract(Name = "section", Namespace = "")] -public class ManifestSection : ISection +public class LegacyManifestSection : ISection { [DataMember(Name = "alias")] public string Alias { get; set; } = string.Empty; diff --git a/src/Umbraco.Core/Manifest/PackageManifest.cs b/src/Umbraco.Core/Manifest/LegacyPackageManifest.cs similarity index 90% rename from src/Umbraco.Core/Manifest/PackageManifest.cs rename to src/Umbraco.Core/Manifest/LegacyPackageManifest.cs index 7bf07cfde9..64fd3f6267 100644 --- a/src/Umbraco.Core/Manifest/PackageManifest.cs +++ b/src/Umbraco.Core/Manifest/LegacyPackageManifest.cs @@ -8,7 +8,7 @@ namespace Umbraco.Cms.Core.Manifest; /// Represents the content of a package manifest. /// [DataContract] -public class PackageManifest +public class LegacyPackageManifest { private string? _packageName; @@ -99,17 +99,17 @@ public class PackageManifest /// Gets or sets the content apps listed in the manifest. /// [DataMember(Name = "contentApps")] - public ManifestContentAppDefinition[] ContentApps { get; set; } = Array.Empty(); + public LegacyManifestContentAppDefinition[] ContentApps { get; set; } = Array.Empty(); /// /// Gets or sets the dashboards listed in the manifest. /// [DataMember(Name = "dashboards")] - public ManifestDashboard[] Dashboards { get; set; } = Array.Empty(); + public LegacyManifestDashboard[] Dashboards { get; set; } = Array.Empty(); /// /// Gets or sets the sections listed in the manifest. /// [DataMember(Name = "sections")] - public ManifestSection[] Sections { get; set; } = Array.Empty(); + public LegacyManifestSection[] Sections { get; set; } = Array.Empty(); } diff --git a/src/Umbraco.Core/Models/Mapping/SectionMapDefinition.cs b/src/Umbraco.Core/Models/Mapping/SectionMapDefinition.cs index c64af5ac0a..7e943d1840 100644 --- a/src/Umbraco.Core/Models/Mapping/SectionMapDefinition.cs +++ b/src/Umbraco.Core/Models/Mapping/SectionMapDefinition.cs @@ -20,7 +20,7 @@ public class SectionMapDefinition : IMapDefinition // this is for AutoMapper ReverseMap - but really? mapper.Define(); mapper.Define(); - mapper.Define(Map); + mapper.Define(Map); mapper.Define(); mapper.Define(); mapper.Define(); @@ -30,7 +30,7 @@ public class SectionMapDefinition : IMapDefinition } // Umbraco.Code.MapAll - private static void Map(Section source, ManifestSection target, MapperContext context) + private static void Map(Section source, LegacyManifestSection target, MapperContext context) { target.Alias = source.Alias; target.Name = source.Name; diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditorCollection.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditorCollection.cs index eec435ddf6..746a4e2c5e 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditorCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditorCollection.cs @@ -5,10 +5,10 @@ namespace Umbraco.Cms.Core.PropertyEditors; public class ParameterEditorCollection : BuilderCollectionBase { - public ParameterEditorCollection(DataEditorCollection dataEditors, IManifestParser manifestParser) + public ParameterEditorCollection(DataEditorCollection dataEditors, ILegacyManifestParser legacyManifestParser) : base(() => dataEditors .Where(x => (x.Type & EditorType.MacroParameter) > 0) - .Union(manifestParser.CombinedManifest.PropertyEditors)) + .Union(legacyManifestParser.CombinedManifest.PropertyEditors)) { } diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditorCollection.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditorCollection.cs index ff700431d5..04a111733c 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditorCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditorCollection.cs @@ -6,10 +6,10 @@ namespace Umbraco.Cms.Core.PropertyEditors; public class PropertyEditorCollection : BuilderCollectionBase { - public PropertyEditorCollection(DataEditorCollection dataEditors, IManifestParser manifestParser) + public PropertyEditorCollection(DataEditorCollection dataEditors, ILegacyManifestParser legacyManifestParser) : base(() => dataEditors .Where(x => (x.Type & EditorType.PropertyValue) > 0) - .Union(manifestParser.CombinedManifest.PropertyEditors)) + .Union(legacyManifestParser.CombinedManifest.PropertyEditors)) { } diff --git a/src/Umbraco.Core/Sections/SectionCollectionBuilder.cs b/src/Umbraco.Core/Sections/SectionCollectionBuilder.cs index 7644b1cc8c..0b8971d223 100644 --- a/src/Umbraco.Core/Sections/SectionCollectionBuilder.cs +++ b/src/Umbraco.Core/Sections/SectionCollectionBuilder.cs @@ -14,8 +14,8 @@ public class // get the manifest parser just-in-time - injecting it in the ctor would mean that // simply getting the builder in order to configure the collection, would require // its dependencies too, and that can create cycles or other oddities - IManifestParser manifestParser = factory.GetRequiredService(); + ILegacyManifestParser legacyManifestParser = factory.GetRequiredService(); - return base.CreateItems(factory).Concat(manifestParser.CombinedManifest.Sections); + return base.CreateItems(factory).Concat(legacyManifestParser.CombinedManifest.Sections); } } diff --git a/src/Umbraco.Core/Telemetry/TelemetryService.cs b/src/Umbraco.Core/Telemetry/TelemetryService.cs index 09ab4483de..38537f080f 100644 --- a/src/Umbraco.Core/Telemetry/TelemetryService.cs +++ b/src/Umbraco.Core/Telemetry/TelemetryService.cs @@ -24,13 +24,13 @@ internal class TelemetryService : ITelemetryService [Obsolete("Please use the constructor that does not take an IManifestParser. Will be removed in V15.")] public TelemetryService( - IManifestParser manifestParser, + ILegacyManifestParser legacyManifestParser, IUmbracoVersion umbracoVersion, ISiteIdentifierService siteIdentifierService, IUsageInformationService usageInformationService, IMetricsConsentService metricsConsentService) : this( - manifestParser, + legacyManifestParser, umbracoVersion, siteIdentifierService, usageInformationService, @@ -41,7 +41,7 @@ internal class TelemetryService : ITelemetryService [Obsolete("Please use the constructor that does not take an IManifestParser. Will be removed in V15.")] public TelemetryService( - IManifestParser manifestParser, + ILegacyManifestParser legacyManifestParser, IUmbracoVersion umbracoVersion, ISiteIdentifierService siteIdentifierService, IUsageInformationService usageInformationService, diff --git a/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml b/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml index 7eb0063a32..d625ef54df 100644 --- a/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml +++ b/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml @@ -21,6 +21,13 @@ lib/net7.0/Umbraco.Infrastructure.dll true + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestParser + lib/net7.0/Umbraco.Infrastructure.dll + lib/net7.0/Umbraco.Infrastructure.dll + true + CP0001 T:Umbraco.Cms.Infrastructure.Migrations.PostMigrations.ClearCsrfCookies @@ -77,6 +84,13 @@ lib/net7.0/Umbraco.Infrastructure.dll true + + CP0002 + M:Umbraco.Cms.Core.Services.Implement.PackagingService.#ctor(Umbraco.Cms.Core.Services.IAuditService,Umbraco.Cms.Core.Packaging.ICreatedPackagesRepository,Umbraco.Cms.Core.Packaging.IPackageInstallation,Umbraco.Cms.Core.Events.IEventAggregator,Umbraco.Cms.Core.Manifest.IManifestParser,Umbraco.Cms.Core.Services.IKeyValueService,Umbraco.Cms.Core.Packaging.PackageMigrationPlanCollection) + lib/net7.0/Umbraco.Infrastructure.dll + lib/net7.0/Umbraco.Infrastructure.dll + true + CP0002 M:Umbraco.Cms.Infrastructure.Install.PackageMigrationRunner.#ctor(Umbraco.Cms.Core.Logging.IProfilingLogger,Umbraco.Cms.Core.Scoping.ICoreScopeProvider,Umbraco.Cms.Core.Packaging.PendingPackageMigrations,Umbraco.Cms.Core.Packaging.PackageMigrationPlanCollection,Umbraco.Cms.Core.Migrations.IMigrationPlanExecutor,Umbraco.Cms.Core.Services.IKeyValueService,Umbraco.Cms.Core.Events.IEventAggregator) @@ -133,6 +147,20 @@ lib/net7.0/Umbraco.Infrastructure.dll true + + CP0002 + M:Umbraco.Cms.Infrastructure.WebAssets.BackOfficeWebAssets.#ctor(Umbraco.Cms.Core.WebAssets.IRuntimeMinifier,Umbraco.Cms.Core.Manifest.IManifestParser,Umbraco.Cms.Core.PropertyEditors.PropertyEditorCollection,Umbraco.Cms.Core.Hosting.IHostingEnvironment,Microsoft.Extensions.Options.IOptionsMonitor{Umbraco.Cms.Core.Configuration.Models.GlobalSettings},Umbraco.Cms.Core.WebAssets.CustomBackOfficeAssetsCollection) + lib/net7.0/Umbraco.Infrastructure.dll + lib/net7.0/Umbraco.Infrastructure.dll + true + + + CP0002 + M:Umbraco.Cms.Infrastructure.WebAssets.BackOfficeWebAssets.GetIndependentPackageBundleName(Umbraco.Cms.Core.Manifest.ManifestAssets,Umbraco.Cms.Core.WebAssets.AssetType) + lib/net7.0/Umbraco.Infrastructure.dll + lib/net7.0/Umbraco.Infrastructure.dll + true + CP0006 M:Umbraco.Cms.Core.Deploy.IGridCellValueConnector.GetValue(Umbraco.Cms.Core.Models.GridValue.GridControl,System.Collections.Generic.ICollection{Umbraco.Cms.Core.Deploy.ArtifactDependency},Umbraco.Cms.Core.Deploy.IContextCache) diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 2b5a4188bf..8507d93184 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -128,7 +128,7 @@ public static partial class UmbracoBuilderExtensions builder.Services.AddTransient(); // register manifest parser, will be injected in collection builders where needed - builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs b/src/Umbraco.Infrastructure/Manifest/LegacyManifestParser.cs similarity index 81% rename from src/Umbraco.Infrastructure/Manifest/ManifestParser.cs rename to src/Umbraco.Infrastructure/Manifest/LegacyManifestParser.cs index 887ac05dc4..76168320b0 100644 --- a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs +++ b/src/Umbraco.Infrastructure/Manifest/LegacyManifestParser.cs @@ -19,33 +19,33 @@ namespace Umbraco.Cms.Core.Manifest; /// /// Parses the Main.js file and replaces all tokens accordingly. /// -public class ManifestParser : IManifestParser +public class LegacyManifestParser : ILegacyManifestParser { private static readonly string _utf8Preamble = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble()); private readonly IAppPolicyCache _cache; private readonly IDataValueEditorFactory _dataValueEditorFactory; private readonly IManifestFileProviderFactory _manifestFileProviderFactory; - private readonly ManifestFilterCollection _filters; + private readonly LegacyManifestFilterCollection _filters; private readonly IHostingEnvironment _hostingEnvironment; private readonly IIOHelper _ioHelper; private readonly IJsonSerializer _jsonSerializer; private readonly ILocalizedTextService _localizedTextService; - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly IShortStringHelper _shortStringHelper; private readonly ManifestValueValidatorCollection _validators; private string _path = null!; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public ManifestParser( + public LegacyManifestParser( AppCaches appCaches, ManifestValueValidatorCollection validators, - ManifestFilterCollection filters, - ILogger logger, + LegacyManifestFilterCollection filters, + ILogger logger, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, IJsonSerializer jsonSerializer, @@ -74,11 +74,11 @@ public class ManifestParser : IManifestParser } [Obsolete("Use other ctor - Will be removed in Umbraco 13")] - public ManifestParser( + public LegacyManifestParser( AppCaches appCaches, ManifestValueValidatorCollection validators, - ManifestFilterCollection filters, - ILogger logger, + LegacyManifestFilterCollection filters, + ILogger logger, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, IJsonSerializer jsonSerializer, @@ -110,19 +110,19 @@ public class ManifestParser : IManifestParser /// Gets all manifests, merged into a single manifest object. /// /// - public CompositePackageManifest CombinedManifest + public CompositeLegacyPackageManifest CombinedManifest => _cache.GetCacheItem("Umbraco.Core.Manifest.ManifestParser::Manifests", () => { - IEnumerable manifests = GetManifests(); + IEnumerable manifests = GetManifests(); return MergeManifests(manifests); }, new TimeSpan(0, 4, 0))!; /// /// Gets all manifests. /// - public IEnumerable GetManifests() + public IEnumerable GetManifests() { - var manifests = new List(); + var manifests = new List(); IFileProvider? manifestFileProvider = _manifestFileProviderFactory.Create(); if (manifestFileProvider is null) @@ -143,7 +143,7 @@ public class ManifestParser : IManifestParser continue; } - PackageManifest manifest = ParseManifest(text); + LegacyPackageManifest manifest = ParseManifest(text); manifest.Source = file.PhysicalPath!; // We assure that the PhysicalPath is not null in GetManifestFiles() manifests.Add(manifest); } @@ -161,7 +161,7 @@ public class ManifestParser : IManifestParser /// /// Parses a manifest. /// - public PackageManifest ParseManifest(string text) + public LegacyPackageManifest ParseManifest(string text) { if (text == null) { @@ -173,7 +173,7 @@ public class ManifestParser : IManifestParser throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(text)); } - PackageManifest? manifest = JsonConvert.DeserializeObject( + LegacyPackageManifest? manifest = JsonConvert.DeserializeObject( text, new DataEditorConverter(_dataValueEditorFactory, _ioHelper, _localizedTextService, _shortStringHelper, _jsonSerializer), new ValueValidatorConverter(_validators), @@ -190,12 +190,12 @@ public class ManifestParser : IManifestParser manifest.Stylesheets[i] = _ioHelper.ResolveRelativeOrVirtualUrl(manifest.Stylesheets[i])!; } - foreach (ManifestContentAppDefinition contentApp in manifest.ContentApps) + foreach (LegacyManifestContentAppDefinition contentApp in manifest.ContentApps) { contentApp.View = _ioHelper.ResolveRelativeOrVirtualUrl(contentApp.View); } - foreach (ManifestDashboard dashboard in manifest.Dashboards) + foreach (LegacyManifestDashboard dashboard in manifest.Dashboards) { dashboard.View = _ioHelper.ResolveRelativeOrVirtualUrl(dashboard.View)!; } @@ -220,34 +220,34 @@ public class ManifestParser : IManifestParser /// /// Merges all manifests into one. /// - private static CompositePackageManifest MergeManifests(IEnumerable manifests) + private static CompositeLegacyPackageManifest MergeManifests(IEnumerable manifests) { - var scripts = new Dictionary>(); - var stylesheets = new Dictionary>(); + var scripts = new Dictionary>(); + var stylesheets = new Dictionary>(); var propertyEditors = new List(); var parameterEditors = new List(); var gridEditors = new List(); - var contentApps = new List(); - var dashboards = new List(); - var sections = new List(); + var contentApps = new List(); + var dashboards = new List(); + var sections = new List(); - foreach (PackageManifest manifest in manifests) + foreach (LegacyPackageManifest manifest in manifests) { - if (!scripts.TryGetValue(manifest.BundleOptions, out List? scriptsPerBundleOption)) + if (!scripts.TryGetValue(manifest.BundleOptions, out List? scriptsPerBundleOption)) { - scriptsPerBundleOption = new List(); + scriptsPerBundleOption = new List(); scripts[manifest.BundleOptions] = scriptsPerBundleOption; } - scriptsPerBundleOption.Add(new ManifestAssets(manifest.PackageName, manifest.Scripts)); + scriptsPerBundleOption.Add(new LegacyManifestAssets(manifest.PackageName, manifest.Scripts)); - if (!stylesheets.TryGetValue(manifest.BundleOptions, out List? stylesPerBundleOption)) + if (!stylesheets.TryGetValue(manifest.BundleOptions, out List? stylesPerBundleOption)) { - stylesPerBundleOption = new List(); + stylesPerBundleOption = new List(); stylesheets[manifest.BundleOptions] = stylesPerBundleOption; } - stylesPerBundleOption.Add(new ManifestAssets(manifest.PackageName, manifest.Stylesheets)); + stylesPerBundleOption.Add(new LegacyManifestAssets(manifest.PackageName, manifest.Stylesheets)); propertyEditors.AddRange(manifest.PropertyEditors); @@ -262,15 +262,15 @@ public class ManifestParser : IManifestParser sections.AddRange(manifest.Sections.DistinctBy(x => x.Alias, StringComparer.OrdinalIgnoreCase)); } - return new CompositePackageManifest( + return new CompositeLegacyPackageManifest( propertyEditors, parameterEditors, gridEditors, contentApps, dashboards, sections, - scripts.ToDictionary(x => x.Key, x => (IReadOnlyList)x.Value), - stylesheets.ToDictionary(x => x.Key, x => (IReadOnlyList)x.Value)); + scripts.ToDictionary(x => x.Key, x => (IReadOnlyList)x.Value), + stylesheets.ToDictionary(x => x.Key, x => (IReadOnlyList)x.Value)); } private static string TrimPreamble(string text) diff --git a/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs b/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs index a0330d75fd..c3dbc5b6f5 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs @@ -20,7 +20,7 @@ public class PackagingService : IPackagingService private readonly ICreatedPackagesRepository _createdPackages; private readonly IEventAggregator _eventAggregator; private readonly IKeyValueService _keyValueService; - private readonly IManifestParser _manifestParser; + private readonly ILegacyManifestParser _legacyManifestParser; private readonly IPackageInstallation _packageInstallation; private readonly PackageMigrationPlanCollection _packageMigrationPlans; @@ -29,7 +29,7 @@ public class PackagingService : IPackagingService ICreatedPackagesRepository createdPackages, IPackageInstallation packageInstallation, IEventAggregator eventAggregator, - IManifestParser manifestParser, + ILegacyManifestParser legacyManifestParser, IKeyValueService keyValueService, PackageMigrationPlanCollection packageMigrationPlans) { @@ -37,7 +37,7 @@ public class PackagingService : IPackagingService _createdPackages = createdPackages; _packageInstallation = packageInstallation; _eventAggregator = eventAggregator; - _manifestParser = manifestParser; + _legacyManifestParser = legacyManifestParser; _keyValueService = keyValueService; _packageMigrationPlans = packageMigrationPlans; } @@ -148,7 +148,7 @@ public class PackagingService : IPackagingService } // Collect and merge the packages from the manifests - foreach (PackageManifest package in _manifestParser.GetManifests()) + foreach (LegacyPackageManifest package in _legacyManifestParser.GetManifests()) { if (package.PackageName is null) { diff --git a/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs b/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs index ab25dc691c..3bb9872901 100644 --- a/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs +++ b/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs @@ -24,7 +24,7 @@ public class BackOfficeWebAssets public const string UmbracoUpgradeCssBundleName = "umbraco-authorize-upgrade-css"; private readonly CustomBackOfficeAssetsCollection _customBackOfficeAssetsCollection; private readonly IHostingEnvironment _hostingEnvironment; - private readonly IManifestParser _parser; + private readonly ILegacyManifestParser _parser; private readonly PropertyEditorCollection _propertyEditorCollection; private readonly IRuntimeMinifier _runtimeMinifier; @@ -32,7 +32,7 @@ public class BackOfficeWebAssets public BackOfficeWebAssets( IRuntimeMinifier runtimeMinifier, - IManifestParser parser, + ILegacyManifestParser parser, PropertyEditorCollection propertyEditorCollection, IHostingEnvironment hostingEnvironment, IOptionsMonitor globalSettings, @@ -48,8 +48,8 @@ public class BackOfficeWebAssets globalSettings.OnChange(x => _globalSettings = x); } - public static string GetIndependentPackageBundleName(ManifestAssets manifestAssets, AssetType assetType) - => $"{manifestAssets.PackageName.ToLowerInvariant()}-{(assetType == AssetType.Css ? "css" : "js")}"; + public static string GetIndependentPackageBundleName(LegacyManifestAssets legacyManifestAssets, AssetType assetType) + => $"{legacyManifestAssets.PackageName.ToLowerInvariant()}-{(assetType == AssetType.Css ? "css" : "js")}"; public void CreateBundles() { @@ -151,7 +151,7 @@ public class BackOfficeWebAssets } private void RegisterPackageBundlesForNoneOption( - IReadOnlyDictionary>? combinedPackageManifestAssets, + IReadOnlyDictionary>? combinedPackageManifestAssets, string bundleName) { var assets = new HashSet(StringComparer.InvariantCultureIgnoreCase); @@ -159,7 +159,7 @@ public class BackOfficeWebAssets // Create a bundle per package manifest that is declaring the matching BundleOptions if (combinedPackageManifestAssets?.TryGetValue( BundleOptions.None, - out IReadOnlyList? manifestAssetList) ?? false) + out IReadOnlyList? manifestAssetList) ?? false) { foreach (var asset in manifestAssetList.SelectMany(x => x.Assets)) { @@ -176,15 +176,15 @@ public class BackOfficeWebAssets } private void RegisterPackageBundlesForIndependentOptions( - IReadOnlyDictionary>? combinedPackageManifestAssets, + IReadOnlyDictionary>? combinedPackageManifestAssets, AssetType assetType) { // Create a bundle per package manifest that is declaring the matching BundleOptions if (combinedPackageManifestAssets?.TryGetValue( BundleOptions.Independent, - out IReadOnlyList? manifestAssetList) ?? false) + out IReadOnlyList? manifestAssetList) ?? false) { - foreach (ManifestAssets manifestAssets in manifestAssetList) + foreach (LegacyManifestAssets manifestAssets in manifestAssetList) { var bundleName = GetIndependentPackageBundleName(manifestAssets, assetType); var filePaths = FormatPaths(manifestAssets.Assets.ToArray()); @@ -215,7 +215,7 @@ public class BackOfficeWebAssets // only include scripts with the default bundle options here if (_parser.CombinedManifest.Scripts.TryGetValue( BundleOptions.Default, - out IReadOnlyList? manifestAssets)) + out IReadOnlyList? manifestAssets)) { foreach (var script in manifestAssets.SelectMany(x => x.Assets)) { @@ -255,7 +255,7 @@ public class BackOfficeWebAssets // only include css with the default bundle options here if (_parser.CombinedManifest.Stylesheets.TryGetValue( BundleOptions.Default, - out IReadOnlyList? manifestAssets)) + out IReadOnlyList? manifestAssets)) { foreach (var script in manifestAssets.SelectMany(x => x.Assets)) { diff --git a/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml b/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml new file mode 100644 index 0000000000..de35de5021 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml @@ -0,0 +1,17 @@ + + + + CP0002 + M:Umbraco.Cms.Web.BackOffice.Controllers.BackOfficeController.#ctor(Umbraco.Cms.Core.Security.IBackOfficeUserManager,Umbraco.Cms.Core.Services.IRuntimeState,Umbraco.Cms.Core.WebAssets.IRuntimeMinifier,Microsoft.Extensions.Options.IOptionsSnapshot{Umbraco.Cms.Core.Configuration.Models.GlobalSettings},Umbraco.Cms.Core.Hosting.IHostingEnvironment,Umbraco.Cms.Core.Services.ILocalizedTextService,Umbraco.Cms.Core.Configuration.Grid.IGridConfig,Umbraco.Cms.Web.BackOffice.Controllers.BackOfficeServerVariables,Umbraco.Cms.Core.Cache.AppCaches,Umbraco.Cms.Web.BackOffice.Security.IBackOfficeSignInManager,Umbraco.Cms.Core.Security.IBackOfficeSecurityAccessor,Microsoft.Extensions.Logging.ILogger{Umbraco.Cms.Web.BackOffice.Controllers.BackOfficeController},Umbraco.Cms.Core.Serialization.IJsonSerializer,Umbraco.Cms.Web.BackOffice.Security.IBackOfficeExternalLoginProviders,Microsoft.AspNetCore.Http.IHttpContextAccessor,Umbraco.Cms.Web.BackOffice.Security.IBackOfficeTwoFactorOptions,Umbraco.Cms.Core.Manifest.IManifestParser,Umbraco.Cms.Infrastructure.WebAssets.ServerVariablesParser,Microsoft.Extensions.Options.IOptions{Umbraco.Cms.Core.Configuration.Models.SecuritySettings}) + lib/net7.0/Umbraco.Web.BackOffice.dll + lib/net7.0/Umbraco.Web.BackOffice.dll + true + + + CP0002 + M:Umbraco.Extensions.RuntimeMinifierExtensions.GetScriptForLoadingBackOfficeAsync(Umbraco.Cms.Core.WebAssets.IRuntimeMinifier,Umbraco.Cms.Core.Configuration.Models.GlobalSettings,Umbraco.Cms.Core.Hosting.IHostingEnvironment,Umbraco.Cms.Core.Manifest.IManifestParser) + lib/net7.0/Umbraco.Web.BackOffice.dll + lib/net7.0/Umbraco.Web.BackOffice.dll + true + + \ No newline at end of file diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index bd40d300c2..7a71242641 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -53,7 +53,7 @@ public class BackOfficeController : UmbracoController private readonly IHttpContextAccessor _httpContextAccessor; private readonly IJsonSerializer _jsonSerializer; private readonly ILogger _logger; - private readonly IManifestParser _manifestParser; + private readonly ILegacyManifestParser _legacyManifestParser; private readonly IRuntimeMinifier _runtimeMinifier; private readonly IRuntimeState _runtimeState; private readonly IOptions _securitySettings; @@ -89,7 +89,7 @@ public class BackOfficeController : UmbracoController IBackOfficeExternalLoginProviders externalLogins, IHttpContextAccessor httpContextAccessor, IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions, - IManifestParser manifestParser, + ILegacyManifestParser legacyManifestParser, ServerVariablesParser serverVariables, IOptions securitySettings) { @@ -109,7 +109,7 @@ public class BackOfficeController : UmbracoController _externalLogins = externalLogins; _httpContextAccessor = httpContextAccessor; _backOfficeTwoFactorOptions = backOfficeTwoFactorOptions; - _manifestParser = manifestParser; + _legacyManifestParser = legacyManifestParser; _serverVariables = serverVariables; _securitySettings = securitySettings; } @@ -236,7 +236,7 @@ public class BackOfficeController : UmbracoController var result = await _runtimeMinifier.GetScriptForLoadingBackOfficeAsync( _globalSettings, _hostingEnvironment, - _manifestParser); + _legacyManifestParser); return new JavaScriptResult(result); } diff --git a/src/Umbraco.Web.BackOffice/Extensions/RuntimeMinifierExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/RuntimeMinifierExtensions.cs index d00d0285e7..95ca6adc10 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/RuntimeMinifierExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/RuntimeMinifierExtensions.cs @@ -17,7 +17,7 @@ public static class RuntimeMinifierExtensions this IRuntimeMinifier minifier, GlobalSettings globalSettings, IHostingEnvironment hostingEnvironment, - IManifestParser manifestParser) + ILegacyManifestParser legacyManifestParser) { var files = new HashSet(StringComparer.InvariantCultureIgnoreCase); foreach (var file in await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoCoreJsBundleName)) @@ -31,10 +31,10 @@ public static class RuntimeMinifierExtensions } // process the independent bundles - if (manifestParser.CombinedManifest.Scripts.TryGetValue(BundleOptions.Independent, - out IReadOnlyList? independentManifestAssetsList)) + if (legacyManifestParser.CombinedManifest.Scripts.TryGetValue(BundleOptions.Independent, + out IReadOnlyList? independentManifestAssetsList)) { - foreach (ManifestAssets manifestAssets in independentManifestAssetsList) + foreach (LegacyManifestAssets manifestAssets in independentManifestAssetsList) { var bundleName = BackOfficeWebAssets.GetIndependentPackageBundleName(manifestAssets, AssetType.Javascript); @@ -58,7 +58,7 @@ public static class RuntimeMinifierExtensions globalSettings, hostingEnvironment); - result += await GetStylesheetInitializationAsync(minifier, manifestParser); + result += await GetStylesheetInitializationAsync(minifier, legacyManifestParser); return result; } @@ -68,7 +68,7 @@ public static class RuntimeMinifierExtensions /// private static async Task GetStylesheetInitializationAsync( IRuntimeMinifier minifier, - IManifestParser manifestParser) + ILegacyManifestParser legacyManifestParser) { var files = new HashSet(StringComparer.InvariantCultureIgnoreCase); foreach (var file in await minifier.GetCssAssetPathsAsync(BackOfficeWebAssets.UmbracoCssBundleName)) @@ -77,10 +77,10 @@ public static class RuntimeMinifierExtensions } // process the independent bundles - if (manifestParser.CombinedManifest.Stylesheets.TryGetValue(BundleOptions.Independent, - out IReadOnlyList? independentManifestAssetsList)) + if (legacyManifestParser.CombinedManifest.Stylesheets.TryGetValue(BundleOptions.Independent, + out IReadOnlyList? independentManifestAssetsList)) { - foreach (ManifestAssets manifestAssets in independentManifestAssetsList) + foreach (LegacyManifestAssets manifestAssets in independentManifestAssetsList) { var bundleName = BackOfficeWebAssets.GetIndependentPackageBundleName(manifestAssets, AssetType.Css); foreach (var asset in await minifier.GetCssAssetPathsAsync(bundleName)) diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestContentAppTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/LegacyManifestContentAppTests.cs similarity index 94% rename from tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestContentAppTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/LegacyManifestContentAppTests.cs index 350c1f1944..5d66ba7556 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestContentAppTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/LegacyManifestContentAppTests.cs @@ -14,7 +14,7 @@ using Umbraco.Cms.Tests.UnitTests.TestHelpers; namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Manifest; [TestFixture] -public class ManifestContentAppTests +public class LegacyManifestContentAppTests { [Test] public void Test() @@ -70,11 +70,11 @@ public class ManifestContentAppTests private void AssertDefinition(object source, bool expected, string[] show, IReadOnlyUserGroup[] groups) { - var definition = JsonConvert.DeserializeObject("{" + + var definition = JsonConvert.DeserializeObject("{" + (show.Length == 0 ? string.Empty : " \"show\": [" + string.Join(",", show.Select(x => "\"" + x + "\"")) + "] ") + "}"); - var factory = new ManifestContentAppFactory(definition, TestHelper.IOHelper); + var factory = new LegacyManifestContentAppFactory(definition, TestHelper.IOHelper); var app = factory.GetContentAppFor(source, groups); if (expected) { diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/LegacyManifestParserTests.cs similarity index 96% rename from tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/LegacyManifestParserTests.cs index 2cb1341f0a..fbef57a11a 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/LegacyManifestParserTests.cs @@ -24,7 +24,7 @@ using Umbraco.Cms.Tests.UnitTests.TestHelpers; namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Manifest; [TestFixture] -public class ManifestParserTests +public class LegacyManifestParserTests { [SetUp] public void Setup() @@ -37,11 +37,11 @@ public class ManifestParserTests }; _ioHelper = TestHelper.IOHelper; var loggerFactory = NullLoggerFactory.Instance; - _parser = new ManifestParser( + _parser = new LegacyManifestParser( AppCaches.Disabled, new ManifestValueValidatorCollection(() => validators), - new ManifestFilterCollection(() => Enumerable.Empty()), - loggerFactory.CreateLogger(), + new LegacyManifestFilterCollection(() => Enumerable.Empty()), + loggerFactory.CreateLogger(), _ioHelper, TestHelper.GetHostingEnvironment(), new JsonNetSerializer(), @@ -51,7 +51,7 @@ public class ManifestParserTests Mock.Of()); } - private ManifestParser _parser; + private LegacyManifestParser _parser; private IIOHelper _ioHelper; [Test] @@ -410,14 +410,14 @@ javascript: ['~/test.js',/*** some note about stuff asd09823-4**09234*/ '~/test2 var manifest = _parser.ParseManifest(json); Assert.AreEqual(2, manifest.ContentApps.Length); - Assert.IsInstanceOf(manifest.ContentApps[0]); + Assert.IsInstanceOf(manifest.ContentApps[0]); var app0 = manifest.ContentApps[0]; Assert.AreEqual("myPackageApp1", app0.Alias); Assert.AreEqual("My App1", app0.Name); Assert.AreEqual("icon-foo", app0.Icon); Assert.AreEqual(_ioHelper.ResolveUrl("/App_Plugins/MyPackage/ContentApps/MyApp1.html"), app0.View); - Assert.IsInstanceOf(manifest.ContentApps[1]); + Assert.IsInstanceOf(manifest.ContentApps[1]); var app1 = manifest.ContentApps[1]; Assert.AreEqual("myPackageApp2", app1.Alias); Assert.AreEqual("My App2", app1.Name); @@ -447,7 +447,7 @@ javascript: ['~/test.js',/*** some note about stuff asd09823-4**09234*/ '~/test2 var manifest = _parser.ParseManifest(json); Assert.AreEqual(2, manifest.Dashboards.Length); - Assert.IsInstanceOf(manifest.Dashboards[0]); + Assert.IsInstanceOf(manifest.Dashboards[0]); var db0 = manifest.Dashboards[0]; Assert.AreEqual("something", db0.Alias); Assert.AreEqual(100, db0.Weight); @@ -460,7 +460,7 @@ javascript: ['~/test.js',/*** some note about stuff asd09823-4**09234*/ '~/test2 Assert.AreEqual(AccessRuleType.Deny, db0.AccessRules[1].Type); Assert.AreEqual("foo", db0.AccessRules[1].Value); - Assert.IsInstanceOf(manifest.Dashboards[1]); + Assert.IsInstanceOf(manifest.Dashboards[1]); var db1 = manifest.Dashboards[1]; Assert.AreEqual("something.else", db1.Alias); Assert.AreEqual(-1, db1.Weight); From e0c396afd64bcafa229cc914faee73725f01468a Mon Sep 17 00:00:00 2001 From: kjac Date: Wed, 15 Feb 2023 15:17:50 +0100 Subject: [PATCH 11/20] Rename PluginConfiguration to PackageManifest + a little more legacy manifest renaming --- .../Package/AllPackageManifestsController.cs | 34 +++++++ .../Package/AllPluginsController.cs | 34 ------- .../PackageBuilderExtensions.cs | 2 +- ... PackageManifestViewModelMapDefinition.cs} | 7 +- ...ewModel.cs => PackageManifestViewModel.cs} | 2 +- ...gacyPackageManifestFileProviderFactory.cs} | 2 +- ...=> IPackageManifestFileProviderFactory.cs} | 2 +- .../Manifest/IPackageManifestService.cs | 6 ++ .../PackageManifest.cs} | 4 +- .../Plugin/IPluginConfigurationService.cs | 6 -- .../Telemetry/TelemetryService.cs | 15 ++- .../UmbracoBuilder.CoreServices.cs | 7 +- .../Manifest/IPackageManifestReader.cs | 8 ++ .../Manifest/LegacyManifestParser.cs | 10 +- .../PackageManifestReader.cs} | 44 ++++----- .../Manifest/PackageManifestService.cs | 24 +++++ .../Plugin/IPluginConfigurationReader.cs | 8 -- .../Plugin/PluginConfigurationService.cs | 24 ----- .../UmbracoBuilderExtensions.cs | 4 +- .../ContentAndWebRootFileProviderFactory.cs | 2 +- .../WebRootFileProviderFactory.cs | 2 +- .../Manifest/LegacyManifestParserTests.cs | 2 +- .../PackageManifestReaderTests.cs} | 92 +++++++++---------- .../Manifest/PackageManifestServiceTests.cs | 67 ++++++++++++++ .../Plugin/PluginConfigurationServiceTests.cs | 67 -------------- .../Telemetry/TelemetryServiceTests.cs | 25 +++-- 26 files changed, 248 insertions(+), 252 deletions(-) create mode 100644 src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackageManifestsController.cs delete mode 100644 src/Umbraco.Cms.Api.Management/Controllers/Package/AllPluginsController.cs rename src/Umbraco.Cms.Api.Management/Mapping/Package/{PluginConfigurationViewModelMapDefinition.cs => PackageManifestViewModelMapDefinition.cs} (53%) rename src/Umbraco.Cms.Api.Management/ViewModels/Package/{PluginConfigurationViewModel.cs => PackageManifestViewModel.cs} (84%) rename src/Umbraco.Core/IO/{IManifestFileProviderFactory.cs => ILegacyPackageManifestFileProviderFactory.cs} (72%) rename src/Umbraco.Core/IO/{IPluginConfigurationFileProviderFactory.cs => IPackageManifestFileProviderFactory.cs} (73%) create mode 100644 src/Umbraco.Core/Manifest/IPackageManifestService.cs rename src/Umbraco.Core/{Plugin/PluginConfiguration.cs => Manifest/PackageManifest.cs} (73%) delete mode 100644 src/Umbraco.Core/Plugin/IPluginConfigurationService.cs create mode 100644 src/Umbraco.Infrastructure/Manifest/IPackageManifestReader.cs rename src/Umbraco.Infrastructure/{Plugin/PluginConfigurationReader.cs => Manifest/PackageManifestReader.cs} (51%) create mode 100644 src/Umbraco.Infrastructure/Manifest/PackageManifestService.cs delete mode 100644 src/Umbraco.Infrastructure/Plugin/IPluginConfigurationReader.cs delete mode 100644 src/Umbraco.Infrastructure/Plugin/PluginConfigurationService.cs rename tests/Umbraco.Tests.UnitTests/Umbraco.Core/{Plugin/PluginConfigurationReaderTests.cs => Manifest/PackageManifestReaderTests.cs} (62%) create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestServiceTests.cs delete mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Plugin/PluginConfigurationServiceTests.cs diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackageManifestsController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackageManifestsController.cs new file mode 100644 index 0000000000..e87acee64d --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackageManifestsController.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Common.ViewModels.Pagination; +using Umbraco.Cms.Api.Management.ViewModels.Package; +using Umbraco.Cms.Core.Manifest; +using Umbraco.Cms.Core.Mapping; + +namespace Umbraco.Cms.Api.Management.Controllers.Package; + +public class AllPackageManifestsController : PackageControllerBase +{ + private readonly IPackageManifestService _packageManifestService; + private readonly IUmbracoMapper _umbracoMapper; + + public AllPackageManifestsController(IPackageManifestService packageManifestService, IUmbracoMapper umbracoMapper) + { + _packageManifestService = packageManifestService; + _umbracoMapper = umbracoMapper; + } + + [HttpGet("manifest")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + public async Task>> AllPackageManifests(int skip = 0, int take = 100) + { + PackageManifest[] packageManifests = (await _packageManifestService.GetPackageManifestsAsync()).ToArray(); + return Ok( + new PagedViewModel + { + Items = _umbracoMapper.MapEnumerable(packageManifests.Skip(skip).Take(take)), + Total = packageManifests.Length + }); + } +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPluginsController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPluginsController.cs deleted file mode 100644 index bbae667125..0000000000 --- a/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPluginsController.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Cms.Api.Common.ViewModels.Pagination; -using Umbraco.Cms.Api.Management.ViewModels.Package; -using Umbraco.Cms.Core.Plugin; -using Umbraco.Cms.Core.Mapping; - -namespace Umbraco.Cms.Api.Management.Controllers.Package; - -public class AllPluginsController : PackageControllerBase -{ - private readonly IPluginConfigurationService _pluginConfigurationService; - private readonly IUmbracoMapper _umbracoMapper; - - public AllPluginsController(IPluginConfigurationService pluginConfigurationService, IUmbracoMapper umbracoMapper) - { - _pluginConfigurationService = pluginConfigurationService; - _umbracoMapper = umbracoMapper; - } - - [HttpGet("plugins")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task>> AllPlugins(int skip = 0, int take = 100) - { - PluginConfiguration[] pluginConfigurations = (await _pluginConfigurationService.GetPluginConfigurationsAsync()).ToArray(); - return Ok( - new PagedViewModel - { - Items = _umbracoMapper.MapEnumerable(pluginConfigurations.Skip(skip).Take(take)), - Total = pluginConfigurations.Length - }); - } -} diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs index 55c8439ec0..24e4c9acc9 100644 --- a/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs +++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs @@ -8,7 +8,7 @@ internal static class PackageBuilderExtensions { internal static IUmbracoBuilder AddPackages(this IUmbracoBuilder builder) { - builder.WithCollectionBuilder().Add(); + builder.WithCollectionBuilder().Add(); return builder; } diff --git a/src/Umbraco.Cms.Api.Management/Mapping/Package/PluginConfigurationViewModelMapDefinition.cs b/src/Umbraco.Cms.Api.Management/Mapping/Package/PackageManifestViewModelMapDefinition.cs similarity index 53% rename from src/Umbraco.Cms.Api.Management/Mapping/Package/PluginConfigurationViewModelMapDefinition.cs rename to src/Umbraco.Cms.Api.Management/Mapping/Package/PackageManifestViewModelMapDefinition.cs index 18318ab3fa..e1ee64af74 100644 --- a/src/Umbraco.Cms.Api.Management/Mapping/Package/PluginConfigurationViewModelMapDefinition.cs +++ b/src/Umbraco.Cms.Api.Management/Mapping/Package/PackageManifestViewModelMapDefinition.cs @@ -1,17 +1,16 @@ using Umbraco.Cms.Api.Management.ViewModels.Package; -using Umbraco.Cms.Core.Plugin; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Mapping; namespace Umbraco.Cms.Api.Management.Mapping.Package; -public class PluginConfigurationViewModelMapDefinition : IMapDefinition +public class PackageManifestViewModelMapDefinition : IMapDefinition { public void DefineMaps(IUmbracoMapper mapper) - => mapper.Define((_, _) => new PluginConfigurationViewModel(), Map); + => mapper.Define((_, _) => new PackageManifestViewModel(), Map); // Umbraco.Code.MapAll - private static void Map(PluginConfiguration source, PluginConfigurationViewModel target, MapperContext context) + private static void Map(PackageManifest source, PackageManifestViewModel target, MapperContext context) { target.Name = source.Name; target.Version = source.Version; diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Package/PluginConfigurationViewModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Package/PackageManifestViewModel.cs similarity index 84% rename from src/Umbraco.Cms.Api.Management/ViewModels/Package/PluginConfigurationViewModel.cs rename to src/Umbraco.Cms.Api.Management/ViewModels/Package/PackageManifestViewModel.cs index 034a2c6239..4b20d922bc 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Package/PluginConfigurationViewModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Package/PackageManifestViewModel.cs @@ -1,6 +1,6 @@ namespace Umbraco.Cms.Api.Management.ViewModels.Package; -public class PluginConfigurationViewModel +public class PackageManifestViewModel { public string Name { get; set; } = string.Empty; diff --git a/src/Umbraco.Core/IO/IManifestFileProviderFactory.cs b/src/Umbraco.Core/IO/ILegacyPackageManifestFileProviderFactory.cs similarity index 72% rename from src/Umbraco.Core/IO/IManifestFileProviderFactory.cs rename to src/Umbraco.Core/IO/ILegacyPackageManifestFileProviderFactory.cs index 982b029c27..39414a87d5 100644 --- a/src/Umbraco.Core/IO/IManifestFileProviderFactory.cs +++ b/src/Umbraco.Core/IO/ILegacyPackageManifestFileProviderFactory.cs @@ -5,6 +5,6 @@ namespace Umbraco.Cms.Core.IO; /// /// Factory for creating instances for providing the package.manifest file. /// -public interface IManifestFileProviderFactory : IFileProviderFactory +public interface ILegacyPackageManifestFileProviderFactory : IFileProviderFactory { } diff --git a/src/Umbraco.Core/IO/IPluginConfigurationFileProviderFactory.cs b/src/Umbraco.Core/IO/IPackageManifestFileProviderFactory.cs similarity index 73% rename from src/Umbraco.Core/IO/IPluginConfigurationFileProviderFactory.cs rename to src/Umbraco.Core/IO/IPackageManifestFileProviderFactory.cs index 221ccb3a5f..39afc36d2f 100644 --- a/src/Umbraco.Core/IO/IPluginConfigurationFileProviderFactory.cs +++ b/src/Umbraco.Core/IO/IPackageManifestFileProviderFactory.cs @@ -5,6 +5,6 @@ namespace Umbraco.Cms.Core.IO; /// /// Factory for creating instances for providing the umbraco-package.json file. /// -public interface IPluginConfigurationFileProviderFactory : IFileProviderFactory +public interface IPackageManifestFileProviderFactory : IFileProviderFactory { } diff --git a/src/Umbraco.Core/Manifest/IPackageManifestService.cs b/src/Umbraco.Core/Manifest/IPackageManifestService.cs new file mode 100644 index 0000000000..6588861411 --- /dev/null +++ b/src/Umbraco.Core/Manifest/IPackageManifestService.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Cms.Core.Manifest; + +public interface IPackageManifestService +{ + Task> GetPackageManifestsAsync(); +} diff --git a/src/Umbraco.Core/Plugin/PluginConfiguration.cs b/src/Umbraco.Core/Manifest/PackageManifest.cs similarity index 73% rename from src/Umbraco.Core/Plugin/PluginConfiguration.cs rename to src/Umbraco.Core/Manifest/PackageManifest.cs index f16f0243d6..ba62334240 100644 --- a/src/Umbraco.Core/Plugin/PluginConfiguration.cs +++ b/src/Umbraco.Core/Manifest/PackageManifest.cs @@ -1,6 +1,6 @@ -namespace Umbraco.Cms.Core.Plugin; +namespace Umbraco.Cms.Core.Manifest; -public class PluginConfiguration +public class PackageManifest { public required string Name { get; set; } diff --git a/src/Umbraco.Core/Plugin/IPluginConfigurationService.cs b/src/Umbraco.Core/Plugin/IPluginConfigurationService.cs deleted file mode 100644 index c176c1ba47..0000000000 --- a/src/Umbraco.Core/Plugin/IPluginConfigurationService.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Umbraco.Cms.Core.Plugin; - -public interface IPluginConfigurationService -{ - Task> GetPluginConfigurationsAsync(); -} diff --git a/src/Umbraco.Core/Telemetry/TelemetryService.cs b/src/Umbraco.Core/Telemetry/TelemetryService.cs index 38537f080f..1d5fde2996 100644 --- a/src/Umbraco.Core/Telemetry/TelemetryService.cs +++ b/src/Umbraco.Core/Telemetry/TelemetryService.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Core.Plugin; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -20,7 +19,7 @@ internal class TelemetryService : ITelemetryService private readonly ISiteIdentifierService _siteIdentifierService; private readonly IUmbracoVersion _umbracoVersion; private readonly IUsageInformationService _usageInformationService; - private readonly IPluginConfigurationService _pluginConfigurationService; + private readonly IPackageManifestService _packageManifestService; [Obsolete("Please use the constructor that does not take an IManifestParser. Will be removed in V15.")] public TelemetryService( @@ -35,7 +34,7 @@ internal class TelemetryService : ITelemetryService siteIdentifierService, usageInformationService, metricsConsentService, - StaticServiceProvider.Instance.GetRequiredService()) + StaticServiceProvider.Instance.GetRequiredService()) { } @@ -46,13 +45,13 @@ internal class TelemetryService : ITelemetryService ISiteIdentifierService siteIdentifierService, IUsageInformationService usageInformationService, IMetricsConsentService metricsConsentService, - IPluginConfigurationService pluginConfigurationService) + IPackageManifestService packageManifestService) : this( umbracoVersion, siteIdentifierService, usageInformationService, metricsConsentService, - pluginConfigurationService) + packageManifestService) { } @@ -64,13 +63,13 @@ internal class TelemetryService : ITelemetryService ISiteIdentifierService siteIdentifierService, IUsageInformationService usageInformationService, IMetricsConsentService metricsConsentService, - IPluginConfigurationService pluginConfigurationService) + IPackageManifestService packageManifestService) { _umbracoVersion = umbracoVersion; _siteIdentifierService = siteIdentifierService; _usageInformationService = usageInformationService; _metricsConsentService = metricsConsentService; - _pluginConfigurationService = pluginConfigurationService; + _packageManifestService = packageManifestService; } [Obsolete("Please use GetTelemetryReportDataAsync. Will be removed in V15.")] @@ -108,7 +107,7 @@ internal class TelemetryService : ITelemetryService return null; } - IEnumerable manifests = await _pluginConfigurationService.GetPluginConfigurationsAsync(); + IEnumerable manifests = await _packageManifestService.GetPackageManifestsAsync(); return manifests .Where(manifest => manifest.AllowTelemetry) diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 8507d93184..ffbfacf6d6 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -10,7 +10,6 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.DistributedLocking; using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Plugin; using Umbraco.Cms.Core.Handlers; using Umbraco.Cms.Core.HealthChecks.NotificationMethods; using Umbraco.Cms.Core.Hosting; @@ -40,11 +39,11 @@ using Umbraco.Cms.Core.Trees; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.DistributedLocking; using Umbraco.Cms.Infrastructure.Examine; -using Umbraco.Cms.Infrastructure.Plugin; using Umbraco.Cms.Infrastructure.HealthChecks; using Umbraco.Cms.Infrastructure.HostedServices; using Umbraco.Cms.Infrastructure.Install; using Umbraco.Cms.Infrastructure.Mail; +using Umbraco.Cms.Infrastructure.Manifest; using Umbraco.Cms.Infrastructure.Migrations; using Umbraco.Cms.Infrastructure.Migrations.Install; using Umbraco.Cms.Infrastructure.Migrations.PostMigrations; @@ -129,8 +128,8 @@ public static partial class UmbracoBuilderExtensions // register manifest parser, will be injected in collection builders where needed builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); // register the manifest filter collection builder (collection is empty by default) builder.ManifestFilters(); diff --git a/src/Umbraco.Infrastructure/Manifest/IPackageManifestReader.cs b/src/Umbraco.Infrastructure/Manifest/IPackageManifestReader.cs new file mode 100644 index 0000000000..806bb3bfb5 --- /dev/null +++ b/src/Umbraco.Infrastructure/Manifest/IPackageManifestReader.cs @@ -0,0 +1,8 @@ +using Umbraco.Cms.Core.Manifest; + +namespace Umbraco.Cms.Infrastructure.Manifest; + +public interface IPackageManifestReader +{ + Task> ReadPackageManifestsAsync(); +} diff --git a/src/Umbraco.Infrastructure/Manifest/LegacyManifestParser.cs b/src/Umbraco.Infrastructure/Manifest/LegacyManifestParser.cs index 76168320b0..916378f201 100644 --- a/src/Umbraco.Infrastructure/Manifest/LegacyManifestParser.cs +++ b/src/Umbraco.Infrastructure/Manifest/LegacyManifestParser.cs @@ -25,7 +25,7 @@ public class LegacyManifestParser : ILegacyManifestParser private readonly IAppPolicyCache _cache; private readonly IDataValueEditorFactory _dataValueEditorFactory; - private readonly IManifestFileProviderFactory _manifestFileProviderFactory; + private readonly ILegacyPackageManifestFileProviderFactory _legacyPackageManifestFileProviderFactory; private readonly LegacyManifestFilterCollection _filters; private readonly IHostingEnvironment _hostingEnvironment; @@ -52,7 +52,7 @@ public class LegacyManifestParser : ILegacyManifestParser ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IDataValueEditorFactory dataValueEditorFactory, - IManifestFileProviderFactory manifestFileProviderFactory) + ILegacyPackageManifestFileProviderFactory legacyPackageManifestFileProviderFactory) { if (appCaches == null) { @@ -70,7 +70,7 @@ public class LegacyManifestParser : ILegacyManifestParser _localizedTextService = localizedTextService; _shortStringHelper = shortStringHelper; _dataValueEditorFactory = dataValueEditorFactory; - _manifestFileProviderFactory = manifestFileProviderFactory; + _legacyPackageManifestFileProviderFactory = legacyPackageManifestFileProviderFactory; } [Obsolete("Use other ctor - Will be removed in Umbraco 13")] @@ -96,7 +96,7 @@ public class LegacyManifestParser : ILegacyManifestParser localizedTextService, shortStringHelper, dataValueEditorFactory, - StaticServiceProvider.Instance.GetRequiredService()) + StaticServiceProvider.Instance.GetRequiredService()) { } @@ -123,7 +123,7 @@ public class LegacyManifestParser : ILegacyManifestParser public IEnumerable GetManifests() { var manifests = new List(); - IFileProvider? manifestFileProvider = _manifestFileProviderFactory.Create(); + IFileProvider? manifestFileProvider = _legacyPackageManifestFileProviderFactory.Create(); if (manifestFileProvider is null) { diff --git a/src/Umbraco.Infrastructure/Plugin/PluginConfigurationReader.cs b/src/Umbraco.Infrastructure/Manifest/PackageManifestReader.cs similarity index 51% rename from src/Umbraco.Infrastructure/Plugin/PluginConfigurationReader.cs rename to src/Umbraco.Infrastructure/Manifest/PackageManifestReader.cs index 9fba461a7a..6ef44b0612 100644 --- a/src/Umbraco.Infrastructure/Plugin/PluginConfigurationReader.cs +++ b/src/Umbraco.Infrastructure/Manifest/PackageManifestReader.cs @@ -2,43 +2,43 @@ using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Plugin; using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Serialization; using Umbraco.Extensions; -namespace Umbraco.Cms.Infrastructure.Plugin; +namespace Umbraco.Cms.Infrastructure.Manifest; -internal sealed class PluginConfigurationReader : IPluginConfigurationReader +internal sealed class PackageManifestReader : IPackageManifestReader { - private readonly IPluginConfigurationFileProviderFactory _pluginConfigurationFileProviderFactory; + private readonly IPackageManifestFileProviderFactory _packageManifestFileProviderFactory; private readonly IJsonSerializer _jsonSerializer; - private readonly ILogger _logger; + private readonly ILogger _logger; - public PluginConfigurationReader( - IPluginConfigurationFileProviderFactory pluginConfigurationFileProviderFactory, + public PackageManifestReader( + IPackageManifestFileProviderFactory packageManifestFileProviderFactory, IJsonSerializer jsonSerializer, - ILogger logger) + ILogger logger) { - _pluginConfigurationFileProviderFactory = pluginConfigurationFileProviderFactory; + _packageManifestFileProviderFactory = packageManifestFileProviderFactory; _jsonSerializer = jsonSerializer; _logger = logger; } - public async Task> ReadPluginConfigurationsAsync() + public async Task> ReadPackageManifestsAsync() { - IFileProvider? fileProvider = _pluginConfigurationFileProviderFactory.Create(); + IFileProvider? fileProvider = _packageManifestFileProviderFactory.Create(); if (fileProvider is null) { throw new ArgumentNullException(nameof(fileProvider)); } - IFileInfo[] files = GetAllPluginConfigurationFiles(fileProvider, Constants.SystemDirectories.AppPlugins).ToArray(); - return await ParsePluginConfigurationFiles(files); + IFileInfo[] files = GetAllPackageManifestFiles(fileProvider, Constants.SystemDirectories.AppPlugins).ToArray(); + return await ParsePackageManifestFiles(files); } - private static IEnumerable GetAllPluginConfigurationFiles(IFileProvider fileProvider, string path) + private static IEnumerable GetAllPackageManifestFiles(IFileProvider fileProvider, string path) { const string extensionFileName = "umbraco-package.json"; foreach (IFileInfo fileInfo in fileProvider.GetDirectoryContents(path)) @@ -48,7 +48,7 @@ internal sealed class PluginConfigurationReader : IPluginConfigurationReader var virtualPath = WebPath.Combine(path, fileInfo.Name); // find all extension package configuration files recursively - foreach (IFileInfo nested in GetAllPluginConfigurationFiles(fileProvider, virtualPath)) + foreach (IFileInfo nested in GetAllPackageManifestFiles(fileProvider, virtualPath)) { yield return nested; } @@ -60,9 +60,9 @@ internal sealed class PluginConfigurationReader : IPluginConfigurationReader } } - private async Task> ParsePluginConfigurationFiles(IFileInfo[] files) + private async Task> ParsePackageManifestFiles(IFileInfo[] files) { - var pluginConfigurations = new List(); + var packageManifests = new List(); foreach (IFileInfo fileInfo in files) { string fileContent; @@ -81,18 +81,18 @@ internal sealed class PluginConfigurationReader : IPluginConfigurationReader try { - PluginConfiguration? pluginConfiguration = _jsonSerializer.Deserialize(fileContent); - if (pluginConfiguration != null) + PackageManifest? packageManifest = _jsonSerializer.Deserialize(fileContent); + if (packageManifest != null) { - pluginConfigurations.Add(pluginConfiguration); + packageManifests.Add(packageManifest); } } catch (Exception ex) { - _logger.LogError(ex, "Unable to load plugin configuration file: {FileName}", fileInfo.Name); + _logger.LogError(ex, "Unable to load package manifest file: {FileName}", fileInfo.Name); } } - return pluginConfigurations; + return packageManifests; } } diff --git a/src/Umbraco.Infrastructure/Manifest/PackageManifestService.cs b/src/Umbraco.Infrastructure/Manifest/PackageManifestService.cs new file mode 100644 index 0000000000..dbbac4c5f9 --- /dev/null +++ b/src/Umbraco.Infrastructure/Manifest/PackageManifestService.cs @@ -0,0 +1,24 @@ +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Manifest; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Manifest; + +internal sealed class PackageManifestService : IPackageManifestService +{ + private readonly IPackageManifestReader _packageManifestReader; + private readonly IAppPolicyCache _cache; + + public PackageManifestService(IPackageManifestReader packageManifestReader, AppCaches appCaches) + { + _packageManifestReader = packageManifestReader; + _cache = appCaches.RuntimeCache; + } + + public async Task> GetPackageManifestsAsync() + => await _cache.GetCacheItemAsync( + $"{nameof(PackageManifestService)}-PackageManifests", + async () => await _packageManifestReader.ReadPackageManifestsAsync(), + TimeSpan.FromMinutes(10)) + ?? Array.Empty(); +} diff --git a/src/Umbraco.Infrastructure/Plugin/IPluginConfigurationReader.cs b/src/Umbraco.Infrastructure/Plugin/IPluginConfigurationReader.cs deleted file mode 100644 index f1f012dbe9..0000000000 --- a/src/Umbraco.Infrastructure/Plugin/IPluginConfigurationReader.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Umbraco.Cms.Core.Plugin; - -namespace Umbraco.Cms.Infrastructure.Plugin; - -public interface IPluginConfigurationReader -{ - Task> ReadPluginConfigurationsAsync(); -} diff --git a/src/Umbraco.Infrastructure/Plugin/PluginConfigurationService.cs b/src/Umbraco.Infrastructure/Plugin/PluginConfigurationService.cs deleted file mode 100644 index 06a8dd10fe..0000000000 --- a/src/Umbraco.Infrastructure/Plugin/PluginConfigurationService.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Plugin; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Plugin; - -internal sealed class PluginConfigurationService : IPluginConfigurationService -{ - private readonly IPluginConfigurationReader _pluginConfigurationReader; - private readonly IAppPolicyCache _cache; - - public PluginConfigurationService(IPluginConfigurationReader pluginConfigurationReader, AppCaches appCaches) - { - _pluginConfigurationReader = pluginConfigurationReader; - _cache = appCaches.RuntimeCache; - } - - public async Task> GetPluginConfigurationsAsync() - => await _cache.GetCacheItemAsync( - $"{nameof(PluginConfigurationService)}-PluginConfigurations", - async () => await _pluginConfigurationReader.ReadPluginConfigurationsAsync(), - TimeSpan.FromMinutes(10)) - ?? Array.Empty(); -} diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index 7efcb3e67d..198f3a2aa2 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -149,9 +149,9 @@ public static partial class UmbracoBuilderExtensions // WebRootFileProviderFactory is just a wrapper around the IWebHostEnvironment.WebRootFileProvider, // therefore no need to register it as singleton - builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(); // Must be added here because DbProviderFactories is netstandard 2.1 so cannot exist in Infra for now builder.Services.AddSingleton(factory => new DbProviderFactoryCreator( diff --git a/src/Umbraco.Web.Common/FileProviders/ContentAndWebRootFileProviderFactory.cs b/src/Umbraco.Web.Common/FileProviders/ContentAndWebRootFileProviderFactory.cs index 64263075d8..22c86e49c2 100644 --- a/src/Umbraco.Web.Common/FileProviders/ContentAndWebRootFileProviderFactory.cs +++ b/src/Umbraco.Web.Common/FileProviders/ContentAndWebRootFileProviderFactory.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.IO; namespace Umbraco.Cms.Web.Common.FileProviders; -public class ContentAndWebRootFileProviderFactory : IManifestFileProviderFactory, IPluginConfigurationFileProviderFactory +public class ContentAndWebRootFileProviderFactory : ILegacyPackageManifestFileProviderFactory, IPackageManifestFileProviderFactory { private readonly IWebHostEnvironment _webHostEnvironment; diff --git a/src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs b/src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs index 64824dd090..02fe86f6fd 100644 --- a/src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs +++ b/src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.IO; namespace Umbraco.Cms.Web.Common.FileProviders; -public class WebRootFileProviderFactory : IManifestFileProviderFactory, IGridEditorsConfigFileProviderFactory +public class WebRootFileProviderFactory : ILegacyPackageManifestFileProviderFactory, IGridEditorsConfigFileProviderFactory { private readonly IWebHostEnvironment _webHostEnvironment; diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/LegacyManifestParserTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/LegacyManifestParserTests.cs index fbef57a11a..f8f1f49610 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/LegacyManifestParserTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/LegacyManifestParserTests.cs @@ -48,7 +48,7 @@ public class LegacyManifestParserTests Mock.Of(), Mock.Of(), Mock.Of(), - Mock.Of()); + Mock.Of()); } private LegacyManifestParser _parser; diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Plugin/PluginConfigurationReaderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestReaderTests.cs similarity index 62% rename from tests/Umbraco.Tests.UnitTests/Umbraco.Core/Plugin/PluginConfigurationReaderTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestReaderTests.cs index 0b7e5c982f..d7d7fb405a 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Plugin/PluginConfigurationReaderTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestReaderTests.cs @@ -6,17 +6,17 @@ using Moq; using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Infrastructure.Plugin; +using Umbraco.Cms.Infrastructure.Manifest; using Umbraco.Cms.Infrastructure.Serialization; -namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Plugin; +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Manifest; [TestFixture] -public class PluginConfigurationReaderTests +public class PackageManifestReaderTests { - private IPluginConfigurationReader _reader; + private IPackageManifestReader _reader; private Mock _rootDirectoryContentsMock; - private Mock> _loggerMock; + private Mock> _loggerMock; private Mock _fileProviderMock; [SetUp] @@ -27,82 +27,82 @@ public class PluginConfigurationReaderTests _fileProviderMock .Setup(m => m.GetDirectoryContents(Constants.SystemDirectories.AppPlugins)) .Returns(_rootDirectoryContentsMock.Object); - var fileProviderFactoryMock = new Mock(); + var fileProviderFactoryMock = new Mock(); fileProviderFactoryMock.Setup(m => m.Create()).Returns(_fileProviderMock.Object); - _loggerMock = new Mock>(); - _reader = new PluginConfigurationReader(fileProviderFactoryMock.Object, new SystemTextJsonSerializer(), _loggerMock.Object); + _loggerMock = new Mock>(); + _reader = new PackageManifestReader(fileProviderFactoryMock.Object, new SystemTextJsonSerializer(), _loggerMock.Object); } [Test] - public async Task Can_Read_PluginConfigurations_At_Root() + public async Task Can_Read_PackageManifests_At_Root() { _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) - .Returns(new List { CreatePluginConfigurationFile() }.GetEnumerator()); + .Returns(new List { CreatePackageManifestFile() }.GetEnumerator()); - var result = await _reader.ReadPluginConfigurationsAsync(); + var result = await _reader.ReadPackageManifestsAsync(); Assert.AreEqual(1, result.Count()); var first = result.First(); - Assert.AreEqual("My Plugin Configuration", first.Name); + Assert.AreEqual("My Package", first.Name); Assert.AreEqual("1.2.3", first.Version); Assert.AreEqual(2, first.Extensions.Count()); Assert.IsTrue(first.Extensions.All(e => e is JsonElement)); } [Test] - public async Task Can_Read_PluginConfiguration_In_Root_Directories() + public async Task Can_Read_PackageManifest_In_Root_Directories() { - var plugin1 = CreateDirectoryMock("/my-extension", CreatePluginConfigurationFile(DefaultPluginConfigurationContent("Plugin One"))); - var plugin2 = CreateDirectoryMock("/my-other-extension", CreatePluginConfigurationFile(DefaultPluginConfigurationContent("Plugin Two"))); + var directoryOne = CreateDirectoryMock("/my-extension", CreatePackageManifestFile(DefaultPackageManifestContent("Package One"))); + var directoryTwo = CreateDirectoryMock("/my-other-extension", CreatePackageManifestFile(DefaultPackageManifestContent("Package Two"))); _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) - .Returns(new List { plugin1, plugin2 }.GetEnumerator()); + .Returns(new List { directoryOne, directoryTwo }.GetEnumerator()); - var result = await _reader.ReadPluginConfigurationsAsync(); + var result = await _reader.ReadPackageManifestsAsync(); Assert.AreEqual(2, result.Count()); - Assert.AreEqual("Plugin One", result.First().Name); - Assert.AreEqual("Plugin Two", result.Last().Name); + Assert.AreEqual("Package One", result.First().Name); + Assert.AreEqual("Package Two", result.Last().Name); } [Test] - public async Task Can_Read_PluginConfigurations_Recursively() + public async Task Can_Read_PackageManifests_Recursively() { - var childFolder = CreateDirectoryMock("/my-parent-folder/my-child-folder", CreatePluginConfigurationFile(DefaultPluginConfigurationContent("Nested Plugin"))); + var childFolder = CreateDirectoryMock("/my-parent-folder/my-child-folder", CreatePackageManifestFile(DefaultPackageManifestContent("Nested Package"))); var parentFolder = CreateDirectoryMock("/my-parent-folder", childFolder); _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) .Returns(new List { parentFolder }.GetEnumerator()); - var result = await _reader.ReadPluginConfigurationsAsync(); + var result = await _reader.ReadPackageManifestsAsync(); Assert.AreEqual(1, result.Count()); - Assert.AreEqual("Nested Plugin", result.First().Name); + Assert.AreEqual("Nested Package", result.First().Name); } [Test] public async Task Can_Skip_Empty_Directories() { - var pluginFolder = CreateDirectoryMock("/my-plugin-folder", CreatePluginConfigurationFile(DefaultPluginConfigurationContent("My Plugin"))); + var packageFolder = CreateDirectoryMock("/my-package-folder", CreatePackageManifestFile(DefaultPackageManifestContent("My Package"))); var emptyFolder = CreateDirectoryMock("/my-empty-folder"); _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) - .Returns(new List { emptyFolder, pluginFolder }.GetEnumerator()); + .Returns(new List { emptyFolder, packageFolder }.GetEnumerator()); - var result = await _reader.ReadPluginConfigurationsAsync(); + var result = await _reader.ReadPackageManifestsAsync(); Assert.AreEqual(1, result.Count()); - Assert.AreEqual("My Plugin", result.First().Name); + Assert.AreEqual("My Package", result.First().Name); } [Test] public async Task Can_Skip_Other_Files() { - var pluginFolder = CreateDirectoryMock( - "/my-plugin-folder", + var packageFolder = CreateDirectoryMock( + "/my-package-folder", CreateOtherFile("my.js"), - CreatePluginConfigurationFile(DefaultPluginConfigurationContent("My Plugin"))); + CreatePackageManifestFile(DefaultPackageManifestContent("My Package"))); var otherFolder = CreateDirectoryMock( "/my-empty-folder", CreateOtherFile("some.js"), @@ -110,11 +110,11 @@ public class PluginConfigurationReaderTests _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) - .Returns(new List { otherFolder, pluginFolder }.GetEnumerator()); + .Returns(new List { otherFolder, packageFolder }.GetEnumerator()); - var result = await _reader.ReadPluginConfigurationsAsync(); + var result = await _reader.ReadPackageManifestsAsync(); Assert.AreEqual(1, result.Count()); - Assert.AreEqual("My Plugin", result.First().Name); + Assert.AreEqual("My Package", result.First().Name); } [Test] @@ -126,12 +126,12 @@ public class PluginConfigurationReaderTests .Setup(f => f.GetEnumerator()) .Returns(folders.GetEnumerator()); - var result = await _reader.ReadPluginConfigurationsAsync(); + var result = await _reader.ReadPackageManifestsAsync(); Assert.AreEqual(0, result.Count()); } [Test] - public async Task Cannot_Read_PluginConfiguration_Without_Name() + public async Task Cannot_Read_PackageManifest_Without_Name() { var content = @"{ ""version"": ""1.2.3"", @@ -145,16 +145,16 @@ public class PluginConfigurationReaderTests }"; _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) - .Returns(new List { CreatePluginConfigurationFile(content) }.GetEnumerator()); + .Returns(new List { CreatePackageManifestFile(content) }.GetEnumerator()); - var result = await _reader.ReadPluginConfigurationsAsync(); + var result = await _reader.ReadPackageManifestsAsync(); Assert.AreEqual(0, result.Count()); EnsureLogErrorWasCalled(); } [Test] - public async Task Cannot_Read_PluginConfiguration_Without_Extensions() + public async Task Cannot_Read_PackageManifest_Without_Extensions() { var content = @"{ ""name"": ""Something"", @@ -163,9 +163,9 @@ public class PluginConfigurationReaderTests }"; _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) - .Returns(new List { CreatePluginConfigurationFile(content) }.GetEnumerator()); + .Returns(new List { CreatePackageManifestFile(content) }.GetEnumerator()); - var result = await _reader.ReadPluginConfigurationsAsync(); + var result = await _reader.ReadPackageManifestsAsync(); Assert.AreEqual(0, result.Count()); EnsureLogErrorWasCalled(); @@ -173,13 +173,13 @@ public class PluginConfigurationReaderTests [TestCase("This is not JSON")] [TestCase(@"{""name"": ""invalid-json"", ""version"": ")] - public async Task Cannot_Read_Invalid_PluginConfiguration(string content) + public async Task Cannot_Read_Invalid_PackageManifest(string content) { _rootDirectoryContentsMock .Setup(f => f.GetEnumerator()) - .Returns(new List { CreatePluginConfigurationFile(content) }.GetEnumerator()); + .Returns(new List { CreatePackageManifestFile(content) }.GetEnumerator()); - var result = await _reader.ReadPluginConfigurationsAsync(); + var result = await _reader.ReadPackageManifestsAsync(); Assert.AreEqual(0, result.Count()); EnsureLogErrorWasCalled(); @@ -213,9 +213,9 @@ public class PluginConfigurationReaderTests return fileInfo.Object; } - private IFileInfo CreatePluginConfigurationFile(string? content = null) + private IFileInfo CreatePackageManifestFile(string? content = null) { - content ??= DefaultPluginConfigurationContent(); + content ??= DefaultPackageManifestContent(); var fileInfo = new Mock(); fileInfo.SetupGet(f => f.IsDirectory).Returns(false); @@ -235,7 +235,7 @@ public class PluginConfigurationReaderTests return fileInfo.Object; } - private static string DefaultPluginConfigurationContent(string name = "My Plugin Configuration") + private static string DefaultPackageManifestContent(string name = "My Package") => @"{ ""name"": ""##NAME##"", ""version"": ""1.2.3"", diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestServiceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestServiceTests.cs new file mode 100644 index 0000000000..cca026eb20 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestServiceTests.cs @@ -0,0 +1,67 @@ +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Manifest; +using Umbraco.Cms.Infrastructure.Manifest; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Manifest; + +[TestFixture] +public class PackageManifestServiceTests +{ + private IPackageManifestService _service; + private Mock _readerMock; + private IAppPolicyCache _runtimeCache; + + [SetUp] + public void SetUp() + { + _readerMock = new Mock(); + _readerMock.Setup(r => r.ReadPackageManifestsAsync()).ReturnsAsync( + new[] + { + new PackageManifest { Name = "Test", Extensions = Array.Empty() } + }); + + _runtimeCache = new ObjectCacheAppCache(); + AppCaches appCaches = new AppCaches( + _runtimeCache, + NoAppCache.Instance, + new IsolatedCaches(type => NoAppCache.Instance)); + + _service = new PackageManifestService(_readerMock.Object, appCaches); + } + + [Test] + public async Task Caches_PackageManifests() + { + var result = await _service.GetPackageManifestsAsync(); + Assert.AreEqual(1, result.Count()); + + var result2 = await _service.GetPackageManifestsAsync(); + Assert.AreEqual(1, result2.Count()); + + var result3 = await _service.GetPackageManifestsAsync(); + Assert.AreEqual(1, result3.Count()); + + _readerMock.Verify(r => r.ReadPackageManifestsAsync(), Times.Exactly(1)); + } + + [Test] + public async Task Reloads_PackageManifest_After_Cache_Clear() + { + var result = await _service.GetPackageManifestsAsync(); + Assert.AreEqual(1, result.Count()); + _runtimeCache.Clear(); + + var result2 = await _service.GetPackageManifestsAsync(); + Assert.AreEqual(1, result2.Count()); + _runtimeCache.Clear(); + + var result3 = await _service.GetPackageManifestsAsync(); + Assert.AreEqual(1, result3.Count()); + _runtimeCache.Clear(); + + _readerMock.Verify(r => r.ReadPackageManifestsAsync(), Times.Exactly(3)); + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Plugin/PluginConfigurationServiceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Plugin/PluginConfigurationServiceTests.cs deleted file mode 100644 index 016799f58a..0000000000 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Plugin/PluginConfigurationServiceTests.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Plugin; -using Umbraco.Cms.Infrastructure.Plugin; - -namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Plugin; - -[TestFixture] -public class PluginConfigurationServiceTests -{ - private IPluginConfigurationService _service; - private Mock _readerMock; - private IAppPolicyCache _runtimeCache; - - [SetUp] - public void SetUp() - { - _readerMock = new Mock(); - _readerMock.Setup(r => r.ReadPluginConfigurationsAsync()).ReturnsAsync( - new[] - { - new PluginConfiguration { Name = "Test", Extensions = Array.Empty() } - }); - - _runtimeCache = new ObjectCacheAppCache(); - AppCaches appCaches = new AppCaches( - _runtimeCache, - NoAppCache.Instance, - new IsolatedCaches(type => NoAppCache.Instance)); - - _service = new PluginConfigurationService(_readerMock.Object, appCaches); - } - - [Test] - public async Task CachesExtensionPackageConfigurations() - { - var result = await _service.GetPluginConfigurationsAsync(); - Assert.AreEqual(1, result.Count()); - - var result2 = await _service.GetPluginConfigurationsAsync(); - Assert.AreEqual(1, result2.Count()); - - var result3 = await _service.GetPluginConfigurationsAsync(); - Assert.AreEqual(1, result3.Count()); - - _readerMock.Verify(r => r.ReadPluginConfigurationsAsync(), Times.Exactly(1)); - } - - [Test] - public async Task ReloadsExtensionPackageConfigurationsAfterCacheClear() - { - var result = await _service.GetPluginConfigurationsAsync(); - Assert.AreEqual(1, result.Count()); - _runtimeCache.Clear(); - - var result2 = await _service.GetPluginConfigurationsAsync(); - Assert.AreEqual(1, result2.Count()); - _runtimeCache.Clear(); - - var result3 = await _service.GetPluginConfigurationsAsync(); - Assert.AreEqual(1, result3.Count()); - _runtimeCache.Clear(); - - _readerMock.Verify(r => r.ReadPluginConfigurationsAsync(), Times.Exactly(3)); - } -} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs index f9a69a6aa1..c29590cf0d 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs @@ -1,7 +1,6 @@ using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration; -using Umbraco.Cms.Core.Plugin; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Semver; @@ -24,7 +23,7 @@ public class TelemetryServiceTests siteIdentifierServiceMock.Object, usageInformationServiceMock.Object, Mock.Of(), - Mock.Of()); + Mock.Of()); Guid guid; await sut.GetTelemetryReportDataAsync(); @@ -40,7 +39,7 @@ public class TelemetryServiceTests CreateSiteIdentifierService(false), Mock.Of(), Mock.Of(), - Mock.Of()); + Mock.Of()); var result = await sut.GetTelemetryReportDataAsync(); Assert.IsNull(result); @@ -58,7 +57,7 @@ public class TelemetryServiceTests CreateSiteIdentifierService(), Mock.Of(), metricsConsentService.Object, - Mock.Of()); + Mock.Of()); var result = await sut.GetTelemetryReportDataAsync(); @@ -73,12 +72,12 @@ public class TelemetryServiceTests var versionPackageName = "VersionPackage"; var packageVersion = "1.0.0"; var noVersionPackageName = "NoVersionPackage"; - PluginConfiguration[] manifests = + PackageManifest[] manifests = { new() { Name = versionPackageName, Version = packageVersion, Extensions = Array.Empty()}, new() { Name = noVersionPackageName, Extensions = Array.Empty() }, }; - var extensionManifestService = CreateExtensionManifestService(manifests); + var packageManifestService = CreatePackageManifestService(manifests); var metricsConsentService = new Mock(); metricsConsentService.Setup(x => x.GetConsentLevel()).Returns(TelemetryLevel.Basic); var sut = new TelemetryService( @@ -86,7 +85,7 @@ public class TelemetryServiceTests CreateSiteIdentifierService(), Mock.Of(), metricsConsentService.Object, - extensionManifestService); + packageManifestService); var result = await sut.GetTelemetryReportDataAsync(); @@ -108,12 +107,12 @@ public class TelemetryServiceTests public async Task RespectsAllowPackageTelemetry() { var version = CreateUmbracoVersion(9, 1, 1); - PluginConfiguration[] manifests = + PackageManifest[] manifests = { new() { Name = "DoNotTrack", AllowTelemetry = false, Extensions = Array.Empty() }, new() { Name = "TrackingAllowed", AllowTelemetry = true, Extensions = Array.Empty() }, }; - var extensionManifestService = CreateExtensionManifestService(manifests); + var packageManifestService = CreatePackageManifestService(manifests); var metricsConsentService = new Mock(); metricsConsentService.Setup(x => x.GetConsentLevel()).Returns(TelemetryLevel.Basic); var sut = new TelemetryService( @@ -121,7 +120,7 @@ public class TelemetryServiceTests CreateSiteIdentifierService(), Mock.Of(), metricsConsentService.Object, - extensionManifestService); + packageManifestService); var result = await sut.GetTelemetryReportDataAsync(); @@ -133,10 +132,10 @@ public class TelemetryServiceTests }); } - private IPluginConfigurationService CreateExtensionManifestService(IEnumerable manifests) + private IPackageManifestService CreatePackageManifestService(IEnumerable manifests) { - var mock = new Mock(); - mock.Setup(x => x.GetPluginConfigurationsAsync()).Returns(Task.FromResult(manifests)); + var mock = new Mock(); + mock.Setup(x => x.GetPackageManifestsAsync()).Returns(Task.FromResult(manifests)); return mock.Object; } From e3b216c0876e3f58074d6c531b524b5aa8b69fce Mon Sep 17 00:00:00 2001 From: kjac Date: Thu, 23 Feb 2023 15:36:59 +0100 Subject: [PATCH 12/20] Re-apply missing legacy manifest renaming + re-generate compat suppressions after merge --- .../PackageBuilderExtensions.cs | 5 +- .../CompatibilitySuppressions.xml | 371 ++++++++++++++++++ .../CompatibilitySuppressions.xml | 35 ++ .../Services/Implement/PackagingService.cs | 2 +- .../CompatibilitySuppressions.xml | 14 + 5 files changed, 425 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs index 3fad1574f4..277cff945e 100644 --- a/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs +++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/PackageBuilderExtensions.cs @@ -12,7 +12,10 @@ internal static class PackageBuilderExtensions { builder.Services.AddTransient(); - builder.WithCollectionBuilder().Add(); + builder + .WithCollectionBuilder() + .Add() + .Add(); return builder; } diff --git a/src/Umbraco.Core/CompatibilitySuppressions.xml b/src/Umbraco.Core/CompatibilitySuppressions.xml index e4376074a2..cd2cccc4a4 100644 --- a/src/Umbraco.Core/CompatibilitySuppressions.xml +++ b/src/Umbraco.Core/CompatibilitySuppressions.xml @@ -1,5 +1,278 @@  + + CP0001 + T:Umbraco.Cms.Core.Manifest.CompositePackageManifest + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.IManifestFilter + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.IManifestParser + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.IPackageManifest + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestAssets + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestContentAppDefinition + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestContentAppFactory + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestDashboard + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestFilterCollection + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestFilterCollectionBuilder + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestSection + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Configuration.Grid.GridConfig.#ctor(Umbraco.Cms.Core.Cache.AppCaches,Umbraco.Cms.Core.Manifest.IManifestParser,Umbraco.Cms.Core.Serialization.IJsonSerializer,Umbraco.Cms.Core.Hosting.IHostingEnvironment,Microsoft.Extensions.Logging.ILoggerFactory) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.get_AllowPackageTelemetry + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.get_BundleOptions + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.get_ContentApps + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.get_Dashboards + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.get_GridEditors + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.get_PackageName + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.get_PackageView + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.get_ParameterEditors + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.get_PropertyEditors + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.get_Scripts + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.get_Sections + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.get_Source + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.get_Stylesheets + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.set_AllowPackageTelemetry(System.Boolean) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.set_BundleOptions(Umbraco.Cms.Core.Manifest.BundleOptions) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.set_ContentApps(Umbraco.Cms.Core.Manifest.ManifestContentAppDefinition[]) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.set_Dashboards(Umbraco.Cms.Core.Manifest.ManifestDashboard[]) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.set_GridEditors(Umbraco.Cms.Core.PropertyEditors.GridEditor[]) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.set_PackageName(System.String) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.set_PackageView(System.String) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.set_ParameterEditors(Umbraco.Cms.Core.PropertyEditors.IDataEditor[]) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.set_PropertyEditors(Umbraco.Cms.Core.PropertyEditors.IDataEditor[]) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.set_Scripts(System.String[]) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.set_Sections(Umbraco.Cms.Core.Manifest.ManifestSection[]) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.set_Source(System.String) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Manifest.PackageManifest.set_Stylesheets(System.String[]) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Models.AuditItem.#ctor(System.Int32,Umbraco.Cms.Core.Models.AuditType,System.Int32,System.String,System.String,System.String) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + CP0002 M:Umbraco.Cms.Core.Models.Blocks.BlockGridItem.get_ForceLeft @@ -357,6 +630,27 @@ lib/net7.0/Umbraco.Core.dll true + + CP0002 + M:Umbraco.Cms.Core.PropertyEditors.ParameterEditorCollection.#ctor(Umbraco.Cms.Core.PropertyEditors.DataEditorCollection,Umbraco.Cms.Core.Manifest.IManifestParser) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.PropertyEditors.PropertyEditorCollection.#ctor(Umbraco.Cms.Core.PropertyEditors.DataEditorCollection,Umbraco.Cms.Core.Manifest.IManifestParser) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0002 + M:Umbraco.Cms.Core.Services.Implement.AuditService.#ctor(Umbraco.Cms.Core.Scoping.ICoreScopeProvider,Microsoft.Extensions.Logging.ILoggerFactory,Umbraco.Cms.Core.Events.IEventMessagesFactory,Umbraco.Cms.Core.Persistence.Repositories.IAuditRepository,Umbraco.Cms.Core.Persistence.Repositories.IAuditEntryRepository) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + CP0002 M:Umbraco.Cms.Core.Services.Implement.DataTypeService.#ctor(Umbraco.Cms.Core.PropertyEditors.IDataValueEditorFactory,Umbraco.Cms.Core.Scoping.ICoreScopeProvider,Microsoft.Extensions.Logging.ILoggerFactory,Umbraco.Cms.Core.Events.IEventMessagesFactory,Umbraco.Cms.Core.Persistence.Repositories.IDataTypeRepository,Umbraco.Cms.Core.Persistence.Repositories.IDataTypeContainerRepository,Umbraco.Cms.Core.Persistence.Repositories.IAuditRepository,Umbraco.Cms.Core.Persistence.Repositories.IEntityRepository,Umbraco.Cms.Core.Persistence.Repositories.IContentTypeRepository,Umbraco.Cms.Core.IO.IIOHelper,Umbraco.Cms.Core.Services.ILocalizedTextService,Umbraco.Cms.Core.Services.ILocalizationService,Umbraco.Cms.Core.Strings.IShortStringHelper,Umbraco.Cms.Core.Serialization.IJsonSerializer) @@ -427,6 +721,20 @@ lib/net7.0/Umbraco.Core.dll true + + CP0006 + M:Umbraco.Cms.Core.Persistence.Repositories.ITrackedReferencesRepository.GetPagedDescendantsInReferences(System.Guid,System.Int64,System.Int64,System.Boolean,System.Int64@) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Persistence.Repositories.ITrackedReferencesRepository.GetPagedItemsWithRelations(System.Collections.Generic.ISet{System.Guid},System.Int64,System.Int64,System.Boolean,System.Int64@) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + CP0006 M:Umbraco.Cms.Core.PropertyEditors.IConfigurationEditor.FromConfigurationEditor(System.Collections.Generic.IDictionary{System.String,System.Object}) @@ -588,6 +896,20 @@ lib/net7.0/Umbraco.Core.dll true + + CP0006 + M:Umbraco.Cms.Core.Services.IPackagingService.CreateCreatedPackageAsync(Umbraco.Cms.Core.Packaging.PackageDefinition,System.Int32) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Services.IPackagingService.DeleteCreatedPackageAsync(System.Guid,System.Int32) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + CP0006 M:Umbraco.Cms.Core.Services.IPackagingService.GetCreatedPackageByKey(System.Guid) @@ -595,6 +917,13 @@ lib/net7.0/Umbraco.Core.dll true + + CP0006 + M:Umbraco.Cms.Core.Services.IPackagingService.GetCreatedPackageByKeyAsync(System.Guid) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + CP0006 M:Umbraco.Cms.Core.Services.IPackagingService.GetInstalledPackagesFromMigrationPlans @@ -602,6 +931,48 @@ lib/net7.0/Umbraco.Core.dll true + + CP0006 + M:Umbraco.Cms.Core.Services.IPackagingService.GetInstalledPackagesFromMigrationPlansAsync(System.Int32,System.Int32) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Services.IPackagingService.UpdateCreatedPackageAsync(Umbraco.Cms.Core.Packaging.PackageDefinition,System.Int32) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Services.ITrackedReferencesService.GetPagedDescendantsInReferencesAsync(System.Guid,System.Int64,System.Int64,System.Boolean) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Services.ITrackedReferencesService.GetPagedItemsWithRelationsAsync(System.Collections.Generic.ISet{System.Guid},System.Int64,System.Int64,System.Boolean) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Services.ITrackedReferencesService.GetPagedRelationsForItemAsync(System.Guid,System.Int64,System.Int64,System.Boolean) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Telemetry.ITelemetryService.GetTelemetryReportDataAsync + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + CP0006 P:Umbraco.Cms.Core.Models.IDataType.ConfigurationData diff --git a/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml b/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml index fc303216ba..c015f419d5 100644 --- a/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml +++ b/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml @@ -21,6 +21,13 @@ lib/net7.0/Umbraco.Infrastructure.dll true + + CP0001 + T:Umbraco.Cms.Core.Manifest.ManifestParser + lib/net7.0/Umbraco.Infrastructure.dll + lib/net7.0/Umbraco.Infrastructure.dll + true + CP0001 T:Umbraco.Cms.Infrastructure.Migrations.PostMigrations.ClearCsrfCookies @@ -84,6 +91,13 @@ lib/net7.0/Umbraco.Infrastructure.dll true + + CP0002 + M:Umbraco.Cms.Core.Services.Implement.PackagingService.#ctor(Umbraco.Cms.Core.Services.IAuditService,Umbraco.Cms.Core.Packaging.ICreatedPackagesRepository,Umbraco.Cms.Core.Packaging.IPackageInstallation,Umbraco.Cms.Core.Events.IEventAggregator,Umbraco.Cms.Core.Manifest.IManifestParser,Umbraco.Cms.Core.Services.IKeyValueService,Umbraco.Cms.Core.Packaging.PackageMigrationPlanCollection) + lib/net7.0/Umbraco.Infrastructure.dll + lib/net7.0/Umbraco.Infrastructure.dll + true + CP0002 M:Umbraco.Cms.Infrastructure.Install.PackageMigrationRunner.#ctor(Umbraco.Cms.Core.Logging.IProfilingLogger,Umbraco.Cms.Core.Scoping.ICoreScopeProvider,Umbraco.Cms.Core.Packaging.PendingPackageMigrations,Umbraco.Cms.Core.Packaging.PackageMigrationPlanCollection,Umbraco.Cms.Core.Migrations.IMigrationPlanExecutor,Umbraco.Cms.Core.Services.IKeyValueService,Umbraco.Cms.Core.Events.IEventAggregator) @@ -140,6 +154,27 @@ lib/net7.0/Umbraco.Infrastructure.dll true + + CP0002 + M:Umbraco.Cms.Infrastructure.WebAssets.BackOfficeWebAssets.#ctor(Umbraco.Cms.Core.WebAssets.IRuntimeMinifier,Umbraco.Cms.Core.Manifest.IManifestParser,Umbraco.Cms.Core.PropertyEditors.PropertyEditorCollection,Umbraco.Cms.Core.Hosting.IHostingEnvironment,Microsoft.Extensions.Options.IOptionsMonitor{Umbraco.Cms.Core.Configuration.Models.GlobalSettings},Umbraco.Cms.Core.WebAssets.CustomBackOfficeAssetsCollection) + lib/net7.0/Umbraco.Infrastructure.dll + lib/net7.0/Umbraco.Infrastructure.dll + true + + + CP0002 + M:Umbraco.Cms.Infrastructure.WebAssets.BackOfficeWebAssets.GetIndependentPackageBundleName(Umbraco.Cms.Core.Manifest.ManifestAssets,Umbraco.Cms.Core.WebAssets.AssetType) + lib/net7.0/Umbraco.Infrastructure.dll + lib/net7.0/Umbraco.Infrastructure.dll + true + + + CP0005 + M:Umbraco.Cms.Core.Security.UmbracoUserStore`2.ResolveEntityIdFromIdentityId(System.String) + lib/net7.0/Umbraco.Infrastructure.dll + lib/net7.0/Umbraco.Infrastructure.dll + true + CP0006 M:Umbraco.Cms.Core.Deploy.IGridCellValueConnector.GetValue(Umbraco.Cms.Core.Models.GridValue.GridControl,System.Collections.Generic.ICollection{Umbraco.Cms.Core.Deploy.ArtifactDependency},Umbraco.Cms.Core.Deploy.IContextCache) diff --git a/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs b/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs index 5b77cceae7..cc40b5ae73 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs @@ -57,7 +57,7 @@ public class PackagingService : IPackagingService ICreatedPackagesRepository createdPackages, IPackageInstallation packageInstallation, IEventAggregator eventAggregator, - IManifestParser manifestParser, + ILegacyManifestParser manifestParser, IKeyValueService keyValueService, PackageMigrationPlanCollection packageMigrationPlans) : this( diff --git a/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml b/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml index a4d1773597..5003e09691 100644 --- a/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml +++ b/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml @@ -1,5 +1,12 @@  + + CP0002 + M:Umbraco.Cms.Web.BackOffice.Controllers.BackOfficeController.#ctor(Umbraco.Cms.Core.Security.IBackOfficeUserManager,Umbraco.Cms.Core.Services.IRuntimeState,Umbraco.Cms.Core.WebAssets.IRuntimeMinifier,Microsoft.Extensions.Options.IOptionsSnapshot{Umbraco.Cms.Core.Configuration.Models.GlobalSettings},Umbraco.Cms.Core.Hosting.IHostingEnvironment,Umbraco.Cms.Core.Services.ILocalizedTextService,Umbraco.Cms.Core.Configuration.Grid.IGridConfig,Umbraco.Cms.Web.BackOffice.Controllers.BackOfficeServerVariables,Umbraco.Cms.Core.Cache.AppCaches,Umbraco.Cms.Web.BackOffice.Security.IBackOfficeSignInManager,Umbraco.Cms.Core.Security.IBackOfficeSecurityAccessor,Microsoft.Extensions.Logging.ILogger{Umbraco.Cms.Web.BackOffice.Controllers.BackOfficeController},Umbraco.Cms.Core.Serialization.IJsonSerializer,Umbraco.Cms.Web.BackOffice.Security.IBackOfficeExternalLoginProviders,Microsoft.AspNetCore.Http.IHttpContextAccessor,Umbraco.Cms.Web.BackOffice.Security.IBackOfficeTwoFactorOptions,Umbraco.Cms.Core.Manifest.IManifestParser,Umbraco.Cms.Infrastructure.WebAssets.ServerVariablesParser,Microsoft.Extensions.Options.IOptions{Umbraco.Cms.Core.Configuration.Models.SecuritySettings}) + lib/net7.0/Umbraco.Web.BackOffice.dll + lib/net7.0/Umbraco.Web.BackOffice.dll + true + CP0002 M:Umbraco.Cms.Web.BackOffice.Controllers.ContentController.#ctor(Umbraco.Cms.Core.Dictionary.ICultureDictionary,Microsoft.Extensions.Logging.ILoggerFactory,Umbraco.Cms.Core.Strings.IShortStringHelper,Umbraco.Cms.Core.Events.IEventMessagesFactory,Umbraco.Cms.Core.Services.ILocalizedTextService,Umbraco.Cms.Core.PropertyEditors.PropertyEditorCollection,Umbraco.Cms.Core.Services.IContentService,Umbraco.Cms.Core.Services.IUserService,Umbraco.Cms.Core.Security.IBackOfficeSecurityAccessor,Umbraco.Cms.Core.Services.IContentTypeService,Umbraco.Cms.Core.Mapping.IUmbracoMapper,Umbraco.Cms.Core.Routing.IPublishedUrlProvider,Umbraco.Cms.Core.Services.IDomainService,Umbraco.Cms.Core.Services.IDataTypeService,Umbraco.Cms.Core.Services.ILocalizationService,Umbraco.Cms.Core.Services.IFileService,Umbraco.Cms.Core.Services.INotificationService,Umbraco.Cms.Core.Actions.ActionCollection,Umbraco.Cms.Infrastructure.Persistence.ISqlContext,Umbraco.Cms.Core.Serialization.IJsonSerializer,Umbraco.Cms.Core.Scoping.ICoreScopeProvider,Microsoft.AspNetCore.Authorization.IAuthorizationService,Umbraco.Cms.Core.Services.IContentVersionService) @@ -7,4 +14,11 @@ lib/net7.0/Umbraco.Web.BackOffice.dll true + + CP0002 + M:Umbraco.Extensions.RuntimeMinifierExtensions.GetScriptForLoadingBackOfficeAsync(Umbraco.Cms.Core.WebAssets.IRuntimeMinifier,Umbraco.Cms.Core.Configuration.Models.GlobalSettings,Umbraco.Cms.Core.Hosting.IHostingEnvironment,Umbraco.Cms.Core.Manifest.IManifestParser) + lib/net7.0/Umbraco.Web.BackOffice.dll + lib/net7.0/Umbraco.Web.BackOffice.dll + true + \ No newline at end of file From a70d76e4b515955ccb66bde6093cb2c306a62854 Mon Sep 17 00:00:00 2001 From: kjac Date: Thu, 23 Feb 2023 15:37:15 +0100 Subject: [PATCH 13/20] Renamed controller for consistency --- ...ManifestsController.cs => AllPackageManifestController.cs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/Umbraco.Cms.Api.Management/Controllers/Package/{AllPackageManifestsController.cs => AllPackageManifestController.cs} (86%) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackageManifestsController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackageManifestController.cs similarity index 86% rename from src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackageManifestsController.cs rename to src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackageManifestController.cs index e87acee64d..92a3f2c2e9 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackageManifestsController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackageManifestController.cs @@ -7,12 +7,12 @@ using Umbraco.Cms.Core.Mapping; namespace Umbraco.Cms.Api.Management.Controllers.Package; -public class AllPackageManifestsController : PackageControllerBase +public class AllPackageManifestController : PackageControllerBase { private readonly IPackageManifestService _packageManifestService; private readonly IUmbracoMapper _umbracoMapper; - public AllPackageManifestsController(IPackageManifestService packageManifestService, IUmbracoMapper umbracoMapper) + public AllPackageManifestController(IPackageManifestService packageManifestService, IUmbracoMapper umbracoMapper) { _packageManifestService = packageManifestService; _umbracoMapper = umbracoMapper; From 583b10562dfc1b7511848c1e2ae84d69ead5a411 Mon Sep 17 00:00:00 2001 From: kjac Date: Thu, 23 Feb 2023 15:37:47 +0100 Subject: [PATCH 14/20] Add missing compat suppression --- src/Umbraco.Web.Common/CompatibilitySuppressions.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/Umbraco.Web.Common/CompatibilitySuppressions.xml diff --git a/src/Umbraco.Web.Common/CompatibilitySuppressions.xml b/src/Umbraco.Web.Common/CompatibilitySuppressions.xml new file mode 100644 index 0000000000..d1edc2b5e1 --- /dev/null +++ b/src/Umbraco.Web.Common/CompatibilitySuppressions.xml @@ -0,0 +1,10 @@ + + + + CP0002 + M:Umbraco.Cms.Web.Common.Profiler.WebProfiler.#ctor + lib/net7.0/Umbraco.Web.Common.dll + lib/net7.0/Umbraco.Web.Common.dll + true + + \ No newline at end of file From 997f56830363ab9cca4f38124a73925a5d20e356 Mon Sep 17 00:00:00 2001 From: kjac Date: Thu, 23 Feb 2023 16:49:20 +0100 Subject: [PATCH 15/20] Make the package manifest endpoint non-paginated --- .../Package/AllPackageManifestController.cs | 13 ++---- src/Umbraco.Cms.Api.Management/OpenApi.json | 44 +++++++++++++++++++ 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackageManifestController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackageManifestController.cs index 92a3f2c2e9..d4b36fd9e9 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackageManifestController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Package/AllPackageManifestController.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Umbraco.Cms.Api.Common.ViewModels.Pagination; using Umbraco.Cms.Api.Management.ViewModels.Package; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Mapping; @@ -18,17 +17,13 @@ public class AllPackageManifestController : PackageControllerBase _umbracoMapper = umbracoMapper; } + // NOTE: this endpoint is deliberately created as non-paginated to ensure the fastest possible client initialization [HttpGet("manifest")] [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task>> AllPackageManifests(int skip = 0, int take = 100) + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task AllPackageManifests() { PackageManifest[] packageManifests = (await _packageManifestService.GetPackageManifestsAsync()).ToArray(); - return Ok( - new PagedViewModel - { - Items = _umbracoMapper.MapEnumerable(packageManifests.Skip(skip).Take(take)), - Total = packageManifests.Length - }); + return Ok(_umbracoMapper.MapEnumerable(packageManifests)); } } diff --git a/src/Umbraco.Cms.Api.Management/OpenApi.json b/src/Umbraco.Cms.Api.Management/OpenApi.json index 52824318c7..e80894a676 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi.json +++ b/src/Umbraco.Cms.Api.Management/OpenApi.json @@ -3932,6 +3932,33 @@ } } }, + "/umbraco/management/api/v1/package/manifest": { + "get": { + "tags": [ + "Package" + ], + "operationId": "GetPackageManifest", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/PackageManifestModel" + } + ] + } + } + } + } + } + } + } + }, "/umbraco/management/api/v1/package/migration-status": { "get": { "tags": [ @@ -7708,6 +7735,23 @@ }, "additionalProperties": false }, + "PackageManifestModel": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string", + "nullable": true + }, + "extensions": { + "type": "array", + "items": { } + } + }, + "additionalProperties": false + }, "PackageMigrationStatusModel": { "type": "object", "properties": { From 95a56238c9850dce8cb083d8796ba48ff5946028 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 27 Feb 2023 14:42:49 +0100 Subject: [PATCH 16/20] update git submodule --- src/Umbraco.Web.UI.New.Client | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.New.Client b/src/Umbraco.Web.UI.New.Client index 7f380e2591..25a7a12993 160000 --- a/src/Umbraco.Web.UI.New.Client +++ b/src/Umbraco.Web.UI.New.Client @@ -1 +1 @@ -Subproject commit 7f380e2591cf3a9c34a8995a2ddf040ee3a4a0f6 +Subproject commit 25a7a12993cc27c41c4f817a72352a2e09adc4bb From b5de843b8479d5657f98a499d3f478bc486f4f76 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 28 Feb 2023 07:55:10 +0100 Subject: [PATCH 17/20] Update src/Umbraco.Infrastructure/Manifest/PackageManifestService.cs Co-authored-by: Bjarke Berg --- .../Manifest/PackageManifestService.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Infrastructure/Manifest/PackageManifestService.cs b/src/Umbraco.Infrastructure/Manifest/PackageManifestService.cs index dbbac4c5f9..180e8a0f10 100644 --- a/src/Umbraco.Infrastructure/Manifest/PackageManifestService.cs +++ b/src/Umbraco.Infrastructure/Manifest/PackageManifestService.cs @@ -6,19 +6,27 @@ namespace Umbraco.Cms.Infrastructure.Manifest; internal sealed class PackageManifestService : IPackageManifestService { - private readonly IPackageManifestReader _packageManifestReader; + private readonly IEnumerable _packageManifestReaders; private readonly IAppPolicyCache _cache; - public PackageManifestService(IPackageManifestReader packageManifestReader, AppCaches appCaches) + public PackageManifestService(IEnumerable packageManifestReaders, AppCaches appCaches) { - _packageManifestReader = packageManifestReader; + _packageManifestReaders = packageManifestReaders; _cache = appCaches.RuntimeCache; } public async Task> GetPackageManifestsAsync() => await _cache.GetCacheItemAsync( $"{nameof(PackageManifestService)}-PackageManifests", - async () => await _packageManifestReader.ReadPackageManifestsAsync(), + async () => + { + var tasks = _packageManifestReaders + .Select(x => x.ReadPackageManifestsAsync()) + .ToArray(); + await Task.WhenAll(tasks); + + return tasks.SelectMany(x => x.Result); + }, TimeSpan.FromMinutes(10)) ?? Array.Empty(); } From 459e6f54219f26f658f5d7e7521725d0a0bebc37 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 28 Feb 2023 08:01:17 +0100 Subject: [PATCH 18/20] Update src/Umbraco.Infrastructure/Manifest/PackageManifestReader.cs Co-authored-by: Bjarke Berg --- src/Umbraco.Infrastructure/Manifest/PackageManifestReader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Infrastructure/Manifest/PackageManifestReader.cs b/src/Umbraco.Infrastructure/Manifest/PackageManifestReader.cs index 6ef44b0612..b4de5c824c 100644 --- a/src/Umbraco.Infrastructure/Manifest/PackageManifestReader.cs +++ b/src/Umbraco.Infrastructure/Manifest/PackageManifestReader.cs @@ -10,7 +10,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Manifest; -internal sealed class PackageManifestReader : IPackageManifestReader +internal sealed class AppPluginsFileProviderPackageManifestReader : IPackageManifestReader { private readonly IPackageManifestFileProviderFactory _packageManifestFileProviderFactory; private readonly IJsonSerializer _jsonSerializer; From 0f95dbbfa664dd996f5276eddd44ef1850508c26 Mon Sep 17 00:00:00 2001 From: kjac Date: Tue, 28 Feb 2023 08:36:48 +0100 Subject: [PATCH 19/20] Review changes and fixes --- .../Models/PackageManifestSettings.cs | 10 ++++++++++ src/Umbraco.Core/Constants-Configuration.cs | 1 + .../UmbracoBuilder.Configuration.cs | 3 ++- .../UmbracoBuilder.CoreServices.cs | 2 +- .../Manifest/PackageManifestReader.cs | 7 ++++--- .../Manifest/PackageManifestService.cs | 15 +++++++++++---- .../Manifest/PackageManifestReaderTests.cs | 18 ++++++------------ .../Manifest/PackageManifestServiceTests.cs | 6 ++++-- 8 files changed, 39 insertions(+), 23 deletions(-) create mode 100644 src/Umbraco.Core/Configuration/Models/PackageManifestSettings.cs diff --git a/src/Umbraco.Core/Configuration/Models/PackageManifestSettings.cs b/src/Umbraco.Core/Configuration/Models/PackageManifestSettings.cs new file mode 100644 index 0000000000..3b712d8eb2 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Models/PackageManifestSettings.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Cms.Core.Configuration.Models; + +/// +/// Typed configuration options for package manifest settings. +/// +[UmbracoOptions(Constants.Configuration.ConfigGlobal)] +public class PackageManifestSettings +{ + public TimeSpan CacheTimeout { get; set; } = TimeSpan.FromMinutes(10); +} diff --git a/src/Umbraco.Core/Constants-Configuration.cs b/src/Umbraco.Core/Constants-Configuration.cs index 656bcd1cdf..782ead11b9 100644 --- a/src/Umbraco.Core/Constants-Configuration.cs +++ b/src/Umbraco.Core/Constants-Configuration.cs @@ -62,6 +62,7 @@ public static partial class Constants public const string ConfigHelpPage = ConfigPrefix + "HelpPage"; public const string ConfigInstallDefaultData = ConfigPrefix + "InstallDefaultData"; public const string ConfigDataTypes = ConfigPrefix + "DataTypes"; + public const string ConfigPackageManifests = ConfigPrefix + "PackageManifests"; public static class NamedOptions { diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs index 92e10c7b1c..a35b38a509 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs @@ -85,7 +85,8 @@ public static partial class UmbracoBuilderExtensions .AddUmbracoOptions() .AddUmbracoOptions() .AddUmbracoOptions() - .AddUmbracoOptions(); + .AddUmbracoOptions() + .AddUmbracoOptions(); // Configure connection string and ensure it's updated when the configuration changes builder.Services.AddSingleton, ConfigureConnectionStrings>(); diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 93d9c52143..73f0b0da49 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -128,7 +128,7 @@ public static partial class UmbracoBuilderExtensions // register manifest parser, will be injected in collection builders where needed builder.Services.AddSingleton(); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); // register the manifest filter collection builder (collection is empty by default) diff --git a/src/Umbraco.Infrastructure/Manifest/PackageManifestReader.cs b/src/Umbraco.Infrastructure/Manifest/PackageManifestReader.cs index b4de5c824c..c87f1ebecf 100644 --- a/src/Umbraco.Infrastructure/Manifest/PackageManifestReader.cs +++ b/src/Umbraco.Infrastructure/Manifest/PackageManifestReader.cs @@ -14,12 +14,12 @@ internal sealed class AppPluginsFileProviderPackageManifestReader : IPackageMani { private readonly IPackageManifestFileProviderFactory _packageManifestFileProviderFactory; private readonly IJsonSerializer _jsonSerializer; - private readonly ILogger _logger; + private readonly ILogger _logger; - public PackageManifestReader( + public AppPluginsFileProviderPackageManifestReader( IPackageManifestFileProviderFactory packageManifestFileProviderFactory, IJsonSerializer jsonSerializer, - ILogger logger) + ILogger logger) { _packageManifestFileProviderFactory = packageManifestFileProviderFactory; _jsonSerializer = jsonSerializer; @@ -90,6 +90,7 @@ internal sealed class AppPluginsFileProviderPackageManifestReader : IPackageMani catch (Exception ex) { _logger.LogError(ex, "Unable to load package manifest file: {FileName}", fileInfo.Name); + throw; } } diff --git a/src/Umbraco.Infrastructure/Manifest/PackageManifestService.cs b/src/Umbraco.Infrastructure/Manifest/PackageManifestService.cs index 180e8a0f10..70c691abe9 100644 --- a/src/Umbraco.Infrastructure/Manifest/PackageManifestService.cs +++ b/src/Umbraco.Infrastructure/Manifest/PackageManifestService.cs @@ -1,4 +1,6 @@ -using Umbraco.Cms.Core.Cache; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Manifest; using Umbraco.Extensions; @@ -8,10 +10,15 @@ internal sealed class PackageManifestService : IPackageManifestService { private readonly IEnumerable _packageManifestReaders; private readonly IAppPolicyCache _cache; + private readonly PackageManifestSettings _packageManifestSettings; - public PackageManifestService(IEnumerable packageManifestReaders, AppCaches appCaches) + public PackageManifestService( + IEnumerable packageManifestReaders, + AppCaches appCaches, + IOptions packageManifestSettings) { _packageManifestReaders = packageManifestReaders; + _packageManifestSettings = packageManifestSettings.Value; _cache = appCaches.RuntimeCache; } @@ -20,13 +27,13 @@ internal sealed class PackageManifestService : IPackageManifestService $"{nameof(PackageManifestService)}-PackageManifests", async () => { - var tasks = _packageManifestReaders + Task>[] tasks = _packageManifestReaders .Select(x => x.ReadPackageManifestsAsync()) .ToArray(); await Task.WhenAll(tasks); return tasks.SelectMany(x => x.Result); }, - TimeSpan.FromMinutes(10)) + _packageManifestSettings.CacheTimeout) ?? Array.Empty(); } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestReaderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestReaderTests.cs index d7d7fb405a..a4b2586e82 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestReaderTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestReaderTests.cs @@ -16,7 +16,7 @@ public class PackageManifestReaderTests { private IPackageManifestReader _reader; private Mock _rootDirectoryContentsMock; - private Mock> _loggerMock; + private Mock> _loggerMock; private Mock _fileProviderMock; [SetUp] @@ -30,8 +30,8 @@ public class PackageManifestReaderTests var fileProviderFactoryMock = new Mock(); fileProviderFactoryMock.Setup(m => m.Create()).Returns(_fileProviderMock.Object); - _loggerMock = new Mock>(); - _reader = new PackageManifestReader(fileProviderFactoryMock.Object, new SystemTextJsonSerializer(), _loggerMock.Object); + _loggerMock = new Mock>(); + _reader = new AppPluginsFileProviderPackageManifestReader(fileProviderFactoryMock.Object, new SystemTextJsonSerializer(), _loggerMock.Object); } [Test] @@ -147,9 +147,7 @@ public class PackageManifestReaderTests .Setup(f => f.GetEnumerator()) .Returns(new List { CreatePackageManifestFile(content) }.GetEnumerator()); - var result = await _reader.ReadPackageManifestsAsync(); - Assert.AreEqual(0, result.Count()); - + Assert.ThrowsAsync(() => _reader.ReadPackageManifestsAsync()); EnsureLogErrorWasCalled(); } @@ -165,9 +163,7 @@ public class PackageManifestReaderTests .Setup(f => f.GetEnumerator()) .Returns(new List { CreatePackageManifestFile(content) }.GetEnumerator()); - var result = await _reader.ReadPackageManifestsAsync(); - Assert.AreEqual(0, result.Count()); - + Assert.ThrowsAsync(() => _reader.ReadPackageManifestsAsync()); EnsureLogErrorWasCalled(); } @@ -179,9 +175,7 @@ public class PackageManifestReaderTests .Setup(f => f.GetEnumerator()) .Returns(new List { CreatePackageManifestFile(content) }.GetEnumerator()); - var result = await _reader.ReadPackageManifestsAsync(); - Assert.AreEqual(0, result.Count()); - + Assert.ThrowsAsync(() => _reader.ReadPackageManifestsAsync()); EnsureLogErrorWasCalled(); } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestServiceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestServiceTests.cs index cca026eb20..419e1e10dd 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestServiceTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/PackageManifestServiceTests.cs @@ -1,6 +1,8 @@ -using Moq; +using Microsoft.Extensions.Options; +using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Infrastructure.Manifest; @@ -29,7 +31,7 @@ public class PackageManifestServiceTests NoAppCache.Instance, new IsolatedCaches(type => NoAppCache.Instance)); - _service = new PackageManifestService(_readerMock.Object, appCaches); + _service = new PackageManifestService(new[] { _readerMock.Object }, appCaches, new OptionsWrapper(new PackageManifestSettings())); } [Test] From b9ffd01f697e1664ac03a19b4ee5d1332682288d Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 28 Feb 2023 09:43:00 +0100 Subject: [PATCH 20/20] Update src/Umbraco.Core/Configuration/Models/PackageManifestSettings.cs Co-authored-by: Bjarke Berg --- .../Configuration/Models/PackageManifestSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Configuration/Models/PackageManifestSettings.cs b/src/Umbraco.Core/Configuration/Models/PackageManifestSettings.cs index 3b712d8eb2..f353281ddd 100644 --- a/src/Umbraco.Core/Configuration/Models/PackageManifestSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/PackageManifestSettings.cs @@ -3,7 +3,7 @@ /// /// Typed configuration options for package manifest settings. /// -[UmbracoOptions(Constants.Configuration.ConfigGlobal)] +[UmbracoOptions(Constants.Configuration.ConfigPackageManifests)] public class PackageManifestSettings { public TimeSpan CacheTimeout { get; set; } = TimeSpan.FromMinutes(10);