diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index 8facd5b5cf..e67a3c08f2 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -715,7 +715,7 @@ stages: dependsOn: - Unit - Integration - - E2E +# - E2E condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.myGetDeploy}})) jobs: - job: diff --git a/src/Umbraco.Core/Constants-Telemetry.cs b/src/Umbraco.Core/Constants-Telemetry.cs index 7875abeb30..b054f78be3 100644 --- a/src/Umbraco.Core/Constants-Telemetry.cs +++ b/src/Umbraco.Core/Constants-Telemetry.cs @@ -31,5 +31,9 @@ public static partial class Constants public static string BackofficeExternalLoginProviderCount = "BackofficeExternalLoginProviderCount"; public static string DeliverApiEnabled = "DeliverApiEnabled"; public static string DeliveryApiPublicAccess = "DeliveryApiPublicAccess"; + public static string WebhookPrefix = "WebhookCount_"; + public static string WebhookTotal = $"{WebhookPrefix}Total"; + public static string WebhookCustomHeaders = $"{WebhookPrefix}CustomHeaders"; + public static string WebhookCustomEvent = $"{WebhookPrefix}CustomEvent"; } } diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml index e719a066fa..5c7b6fb28b 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml @@ -476,7 +476,7 @@
  • Anonymized site ID, Umbraco version, and packages installed.
  • -
  • Number of: Root nodes, Content nodes, Macros, Media, Document Types, Templates, Languages, Domains, User Group, Users, Members, Backoffice external login providers, and Property Editors in use.
  • +
  • Number of: Root nodes, Content nodes, Macros, Media, Document Types, Templates, Languages, Domains, User Group, Users, Members, Backoffice external login providers, Webhooks, and Property Editors in use.
  • System information: Webserver, server OS, server framework, server OS language, and database provider.
  • Configuration settings: Modelsbuilder mode, if custom Umbraco path exists, ASP environment, whether the delivery API is enabled, and allows public access, and if you are in debug mode.
  • diff --git a/src/Umbraco.Infrastructure/BackgroundJobs/Jobs/ReportSiteJob.cs b/src/Umbraco.Infrastructure/BackgroundJobs/Jobs/ReportSiteJob.cs index 1a35c4199c..b3de6fa906 100644 --- a/src/Umbraco.Infrastructure/BackgroundJobs/Jobs/ReportSiteJob.cs +++ b/src/Umbraco.Infrastructure/BackgroundJobs/Jobs/ReportSiteJob.cs @@ -10,19 +10,24 @@ namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs; public class ReportSiteJob : IRecurringBackgroundJob { - public TimeSpan Period { get => TimeSpan.FromDays(1); } - public TimeSpan Delay { get => TimeSpan.FromMinutes(5); } - public ServerRole[] ServerRoles { get => Enum.GetValues(); } + public TimeSpan Period => TimeSpan.FromDays(1); + + public TimeSpan Delay => TimeSpan.FromMinutes(5); + + public ServerRole[] ServerRoles => Enum.GetValues(); // No-op event as the period never changes on this job - public event EventHandler PeriodChanged { add { } remove { } } - + public event EventHandler PeriodChanged + { + add { } remove { } + } private static HttpClient _httpClient = new(); - private readonly ILogger _logger; - private readonly ITelemetryService _telemetryService; - private readonly IJsonSerializer _jsonSerializer; + private readonly ILogger _logger; + + private readonly ITelemetryService _telemetryService; +private readonly IJsonSerializer _jsonSerializer; public ReportSiteJob( ILogger logger, @@ -39,9 +44,8 @@ public class ReportSiteJob : IRecurringBackgroundJob /// Runs the background task to send the anonymous ID /// to telemetry service /// - public async Task RunJobAsync() + public async Task RunJobAsync() { - if (_telemetryService.TryGetTelemetryReportData(out TelemetryReportData? telemetryReportData) is false) { _logger.LogWarning("No telemetry marker found"); diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.TelemetryProviders.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.TelemetryProviders.cs index 8c5651cf3e..a498ee7d05 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.TelemetryProviders.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.TelemetryProviders.cs @@ -19,6 +19,7 @@ public static class UmbracoBuilder_TelemetryProviders builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); + builder.Services.AddTransient(); return builder; } } diff --git a/src/Umbraco.Infrastructure/Telemetry/Providers/WebhookTelemetryProvider.cs b/src/Umbraco.Infrastructure/Telemetry/Providers/WebhookTelemetryProvider.cs new file mode 100644 index 0000000000..9aa5ae74c6 --- /dev/null +++ b/src/Umbraco.Infrastructure/Telemetry/Providers/WebhookTelemetryProvider.cs @@ -0,0 +1,41 @@ +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.Telemetry.Interfaces; + +namespace Umbraco.Cms.Infrastructure.Telemetry.Providers; + +public class WebhookTelemetryProvider : IDetailedTelemetryProvider +{ + private readonly IWebhookService _webhookService; + + public WebhookTelemetryProvider(IWebhookService webhookService) => _webhookService = webhookService; + + private readonly string[] _defaultEventTypes = + [ + "Umbraco.ContentDelete", + "Umbraco.ContentPublish", + "Umbraco.ContentUnpublish", + "Umbraco.MediaDelete", + "Umbraco.MediaSave" + ]; + + public IEnumerable GetInformation() + { + IWebhook[] allWebhooks = _webhookService.GetAllAsync(0, int.MaxValue).GetAwaiter().GetResult().Items.ToArray(); + + yield return new UsageInformation(Constants.Telemetry.WebhookTotal, allWebhooks.Length); + + foreach (var eventType in _defaultEventTypes) + { + IWebhook[] webhooks = allWebhooks.Where(x => x.Events.Contains(eventType)).ToArray(); + yield return new UsageInformation($"{Constants.Telemetry.WebhookPrefix}{eventType}", webhooks.Length); + } + + IEnumerable customWebhooks = allWebhooks.Where(x => x.Events.Except(_defaultEventTypes).Any()); + yield return new UsageInformation(Constants.Telemetry.WebhookCustomEvent, customWebhooks.Count()); + + IEnumerable customHeaderWebhooks = allWebhooks.Where(x => x.Headers.Any()); + yield return new UsageInformation(Constants.Telemetry.WebhookCustomHeaders, customHeaderWebhooks.Count()); + } +} diff --git a/tests/Umbraco.Tests.Integration/DependencyInjection/UmbracoBuilderExtensions.cs b/tests/Umbraco.Tests.Integration/DependencyInjection/UmbracoBuilderExtensions.cs index bd0a8bc5bc..796bbe9ff2 100644 --- a/tests/Umbraco.Tests.Integration/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/tests/Umbraco.Tests.Integration/DependencyInjection/UmbracoBuilderExtensions.cs @@ -120,7 +120,18 @@ public static class UmbracoBuilderExtensions var currFolder = new DirectoryInfo(srcFolder); - var uiProject = currFolder.GetDirectories("Umbraco.Web.UI", SearchOption.TopDirectoryOnly).First(); + if (!currFolder.Exists) + { + currFolder = new DirectoryInfo(Path.GetTempPath()); + } + + var uiProject = currFolder.GetDirectories("Umbraco.Web.UI", SearchOption.TopDirectoryOnly).FirstOrDefault(); + if (uiProject == null) + { + uiProject = new DirectoryInfo(Path.Combine(Path.GetTempPath(), "Umbraco.Web.UI")); + uiProject.Create(); + } + var mainLangFolder = new DirectoryInfo(Path.Combine(uiProject.FullName, globalSettings.Value.UmbracoPath.TrimStart("~/"), "config", "lang")); return new LocalizedTextServiceFileSources( diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Telemetry/TelemetryServiceTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Telemetry/TelemetryServiceTests.cs index f736240e76..0ce5e013f3 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Telemetry/TelemetryServiceTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Telemetry/TelemetryServiceTests.cs @@ -1,12 +1,11 @@ -using System.Linq; using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Telemetry; +using Umbraco.Cms.Core.Webhooks; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; @@ -20,12 +19,15 @@ public class TelemetryServiceTests : UmbracoIntegrationTest builder.Services.Configure(options => options.Id = Guid.NewGuid().ToString()); private ITelemetryService TelemetryService => GetRequiredService(); + private IMetricsConsentService MetricsConsentService => GetRequiredService(); + private WebhookEventCollection WebhookEventCollection => GetRequiredService(); + [Test] public async Task Expected_Detailed_Telemetry_Exists() { - var expectedData = new[] + var expectedData = new List { Constants.Telemetry.RootCount, Constants.Telemetry.DomainCount, @@ -51,9 +53,15 @@ public class TelemetryServiceTests : UmbracoIntegrationTest Constants.Telemetry.BackofficeExternalLoginProviderCount, Constants.Telemetry.RuntimeMode, Constants.Telemetry.DeliverApiEnabled, - Constants.Telemetry.DeliveryApiPublicAccess + Constants.Telemetry.DeliveryApiPublicAccess, + Constants.Telemetry.WebhookTotal, + Constants.Telemetry.WebhookCustomHeaders, + Constants.Telemetry.WebhookCustomEvent, }; + // Add the default webhook events. + expectedData.AddRange(WebhookEventCollection.Select(eventInfo => $"{Constants.Telemetry.WebhookPrefix}{eventInfo.Alias}")); + await MetricsConsentService.SetConsentLevelAsync(TelemetryLevel.Detailed); var success = TelemetryService.TryGetTelemetryReportData(out var telemetryReportData); var detailed = telemetryReportData.Detailed.ToArray(); @@ -62,7 +70,7 @@ public class TelemetryServiceTests : UmbracoIntegrationTest Assert.Multiple(() => { Assert.IsNotNull(detailed); - Assert.AreEqual(expectedData.Length, detailed.Length); + Assert.AreEqual(expectedData.Count, detailed.Length); foreach (var expectedInfo in expectedData) { diff --git a/version.json b/version.json index 5ddc3e1cc6..cf30f7b7d2 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", - "version": "14.0.0-rc1", + "version": "14.1.0-rc", "assemblyVersion": { "precision": "build" },