diff --git a/eFormAPI/Plugins/OuterInnerResource.Pn/OuterInnerResource.Pn.Test/SentryDsnAssemblyMetadataTests.cs b/eFormAPI/Plugins/OuterInnerResource.Pn/OuterInnerResource.Pn.Test/SentryDsnAssemblyMetadataTests.cs new file mode 100644 index 00000000..9d2f418a --- /dev/null +++ b/eFormAPI/Plugins/OuterInnerResource.Pn/OuterInnerResource.Pn.Test/SentryDsnAssemblyMetadataTests.cs @@ -0,0 +1,57 @@ +/* +The MIT License (MIT) + +Copyright (c) 2007 - 2026 Microting A/S + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +using System; +using System.Linq; +using System.Reflection; +using NUnit.Framework; + +namespace MachineArea.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(OuterInnerResource.Pn.EformOuterInnerResourcePlugin).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/OuterInnerResource.Pn/OuterInnerResource.Pn/EformOuterInnerResourcePlugin.cs b/eFormAPI/Plugins/OuterInnerResource.Pn/OuterInnerResource.Pn/EformOuterInnerResourcePlugin.cs index 8ac577a6..cb96f475 100644 --- a/eFormAPI/Plugins/OuterInnerResource.Pn/OuterInnerResource.Pn/EformOuterInnerResourcePlugin.cs +++ b/eFormAPI/Plugins/OuterInnerResource.Pn/OuterInnerResource.Pn/EformOuterInnerResourcePlugin.cs @@ -105,31 +105,41 @@ public void ConfigureOptionsServices( configuration.GetSection("OuterInnerResourceSettings")); } + // 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(EformOuterInnerResourcePlugin).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://9bf9ba91ddb235518478803b2b202fc8@o4506241219428352.ingest.us.sentry.io/4508266875781120"; - - // 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; - - // This option will enable Sentry's tracing features. You still need to start transactions and spans. - //options.EnableTracing = 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; + + // This option will enable Sentry's tracing features. You still need to start transactions and spans. + //options.EnableTracing = true; + }); + } string pattern = @"Database=(\d+)_eform-angular-outer-inner-resource-plugin;"; Match match = Regex.Match(connectionString!, pattern); diff --git a/eFormAPI/Plugins/OuterInnerResource.Pn/OuterInnerResource.Pn/OuterInnerResource.Pn.csproj b/eFormAPI/Plugins/OuterInnerResource.Pn/OuterInnerResource.Pn/OuterInnerResource.Pn.csproj index 98b4238e..18357407 100644 --- a/eFormAPI/Plugins/OuterInnerResource.Pn/OuterInnerResource.Pn/OuterInnerResource.Pn.csproj +++ b/eFormAPI/Plugins/OuterInnerResource.Pn/OuterInnerResource.Pn/OuterInnerResource.Pn.csproj @@ -24,6 +24,10 @@ true + + + +