diff --git a/eFormAPI/Plugins/ItemsPlanning.Pn/ItemsPlanning.Pn.Test/SentryDsnAssemblyMetadataTests.cs b/eFormAPI/Plugins/ItemsPlanning.Pn/ItemsPlanning.Pn.Test/SentryDsnAssemblyMetadataTests.cs new file mode 100644 index 00000000..68b102f9 --- /dev/null +++ b/eFormAPI/Plugins/ItemsPlanning.Pn/ItemsPlanning.Pn.Test/SentryDsnAssemblyMetadataTests.cs @@ -0,0 +1,33 @@ +using System; +using System.Linq; +using System.Reflection; +using NUnit.Framework; + +namespace ItemsPlanning.Pn.Test +{ + /// + /// The API host routes this plugin's Sentry events to the plugin's own project, which it finds through + /// [assembly: AssemblyMetadata("SentryDsn", ...)] on the plugin assembly (an + /// AssemblyMetadata item in the csproj). Only the shape is asserted; the value lives in the csproj. + /// + [TestFixture] + public class SentryDsnAssemblyMetadataTests + { + [Test] + public void PluginAssembly_DeclaresExactlyOneWellFormedSentryDsn() + { + var values = typeof(EformItemsPlanningPlugin).Assembly + .GetCustomAttributes() + .Where(x => x.Key == "SentryDsn") + .Select(x => x.Value) + .ToList(); + + Assert.That(values, Has.Count.EqualTo(1), "exactly one SentryDsn assembly metadata value"); + Assert.That(Uri.TryCreate(values[0], UriKind.Absolute, out var dsn), Is.True, "absolute URI"); + Assert.That(dsn!.Scheme, Is.EqualTo(Uri.UriSchemeHttps)); + Assert.That(dsn.UserInfo, Is.Not.Empty, "public key in the user info"); + Assert.That(dsn.AbsolutePath.Trim('/'), Does.Match(@"^\d+$"), "numeric project id as the path"); + Assert.That(dsn.Host, Does.Match(@"^o\d+\.ingest(\.[a-z]+)?\.sentry\.io$"), "sentry.io ingest host"); + } + } +} diff --git a/eFormAPI/Plugins/ItemsPlanning.Pn/ItemsPlanning.Pn/EformItemsPlanningPlugin.cs b/eFormAPI/Plugins/ItemsPlanning.Pn/ItemsPlanning.Pn/EformItemsPlanningPlugin.cs index 0c0ac1e1..fed60a2e 100644 --- a/eFormAPI/Plugins/ItemsPlanning.Pn/ItemsPlanning.Pn/EformItemsPlanningPlugin.cs +++ b/eFormAPI/Plugins/ItemsPlanning.Pn/ItemsPlanning.Pn/EformItemsPlanningPlugin.cs @@ -121,28 +121,38 @@ public void ConfigureOptionsServices( configuration.GetSection("ItemsPlanningBaseSettings")); } + // Declared once, as in the csproj: the API host reads the + // same assembly metadata to route this plugin's events to its own Sentry project. + private static string SentryDsn => + typeof(EformItemsPlanningPlugin).Assembly + .GetCustomAttributes() + .FirstOrDefault(x => x.Key == "SentryDsn")?.Value; + public void ConfigureDbContext(IServiceCollection services, string connectionString) { - SentrySdk.Init(options => + if (!string.IsNullOrWhiteSpace(SentryDsn)) { - // A Sentry Data Source Name (DSN) is required. - // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ - // You can set it in the SENTRY_DSN environment variable, or you can set it in code here. - options.Dsn = "https://d188dc3ab45515b062955aa299bec1dc@o4506241219428352.ingest.us.sentry.io/4508266878664704"; - - // When debug is enabled, the Sentry client will emit detailed debugging information to the console. - // This might be helpful, or might interfere with the normal operation of your application. - // We enable it here for demonstration purposes when first trying Sentry. - // You shouldn't do this in your applications unless you're troubleshooting issues with Sentry. - options.Debug = false; - - // This option is recommended. It enables Sentry's "Release Health" feature. - options.AutoSessionTracking = true; - - // This option is recommended for client applications only. It ensures all threads use the same global scope. - // If you're writing a background service of any kind, you should remove this. - options.IsGlobalModeEnabled = true; - }); + SentrySdk.Init(options => + { + // A Sentry Data Source Name (DSN) is required. + // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ + // You can set it in the SENTRY_DSN environment variable, or you can set it in code here. + options.Dsn = SentryDsn; + + // When debug is enabled, the Sentry client will emit detailed debugging information to the console. + // This might be helpful, or might interfere with the normal operation of your application. + // We enable it here for demonstration purposes when first trying Sentry. + // You shouldn't do this in your applications unless you're troubleshooting issues with Sentry. + options.Debug = false; + + // This option is recommended. It enables Sentry's "Release Health" feature. + options.AutoSessionTracking = true; + + // This option is recommended for client applications only. It ensures all threads use the same global scope. + // If you're writing a background service of any kind, you should remove this. + options.IsGlobalModeEnabled = true; + }); + } string pattern = @"Database=(\d+)_eform-angular-items-planning-plugin;"; Match match = Regex.Match(connectionString!, pattern); diff --git a/eFormAPI/Plugins/ItemsPlanning.Pn/ItemsPlanning.Pn/ItemsPlanning.Pn.csproj b/eFormAPI/Plugins/ItemsPlanning.Pn/ItemsPlanning.Pn/ItemsPlanning.Pn.csproj index 8039fc77..7f2128b9 100644 --- a/eFormAPI/Plugins/ItemsPlanning.Pn/ItemsPlanning.Pn/ItemsPlanning.Pn.csproj +++ b/eFormAPI/Plugins/ItemsPlanning.Pn/ItemsPlanning.Pn/ItemsPlanning.Pn.csproj @@ -17,6 +17,10 @@ true + + + +