Skip to content

Commit

Permalink
Fix issue where AppInsights SDK doesn't load/configure correctly due …
Browse files Browse the repository at this point in the history
…to keyed services being registered in DI (#594)
  • Loading branch information
martinothamar authored Apr 16, 2024
1 parent e261880 commit dc62cf8
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 18 deletions.
5 changes: 5 additions & 0 deletions src/Altinn.App.Api/Altinn.App.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,9 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="$(AssemblyName).Tests" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
</Project>
8 changes: 7 additions & 1 deletion src/Altinn.App.Api/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,13 @@ public static void AddAltinnAppServices(this IServiceCollection services, IConfi
services.AddMetricsServer(config);
}

private static void AddApplicationInsights(IServiceCollection services, IConfiguration config, IWebHostEnvironment env)
/// <summary>
/// Adds Application Insights to the service collection.
/// </summary>
/// <param name="services">Services</param>
/// <param name="config">Config</param>
/// <param name="env">Environment</param>
internal static void AddApplicationInsights(IServiceCollection services, IConfiguration config, IWebHostEnvironment env)
{
string? applicationInsightsKey = env.IsDevelopment()
? config["ApplicationInsights:InstrumentationKey"]
Expand Down
1 change: 1 addition & 0 deletions src/Altinn.App.Core/Altinn.App.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@

<ItemGroup>
<InternalsVisibleTo Include="$(AssemblyName).Tests" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
</Project>
4 changes: 2 additions & 2 deletions src/Altinn.App.Core/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,8 @@ private static void AddProcessServices(IServiceCollection services)
services.AddTransient<IProcessTask, NullTypeProcessTask>();

//SERVICE TASKS
services.AddKeyedTransient<IServiceTask, PdfServiceTask>("pdfService");
services.AddKeyedTransient<IServiceTask, EformidlingServiceTask>("eFormidlingService");
services.AddTransient<IServiceTask, PdfServiceTask>();
services.AddTransient<IServiceTask, EformidlingServiceTask>();
}

private static void AddActionServices(IServiceCollection services)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Altinn.App.Core.Internal.Process.ProcessTasks;
using Altinn.App.Core.Internal.Process.ServiceTasks;
using Altinn.Platform.Storage.Interface.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Altinn.App.Core.Internal.Process.EventHandlers.ProcessTask
Expand All @@ -25,16 +24,17 @@ public class EndTaskEventHandler : IEndTaskEventHandler
public EndTaskEventHandler(
IProcessTaskDataLocker processTaskDataLocker,
IProcessTaskFinalizer processTaskFinisher,
[FromKeyedServices("pdfService")] IServiceTask pdfServiceTask,
[FromKeyedServices("eFormidlingService")] IServiceTask eformidlingServiceTask,
IEnumerable<IServiceTask> serviceTasks,
IEnumerable<IProcessTaskEnd> processTaskEnds,
ILogger<EndTaskEventHandler> logger
)
{
_processTaskDataLocker = processTaskDataLocker;
_processTaskFinisher = processTaskFinisher;
_pdfServiceTask = pdfServiceTask;
_eformidlingServiceTask = eformidlingServiceTask;
_pdfServiceTask = serviceTasks.FirstOrDefault(x => x is IPdfServiceTask)
?? throw new InvalidOperationException("PdfServiceTask not found in serviceTasks");
_eformidlingServiceTask = serviceTasks.FirstOrDefault(x => x is IEformidlingServiceTask)
?? throw new InvalidOperationException("EformidlingServiceTask not found in serviceTasks");
_processTaskEnds = processTaskEnds;
_logger = logger;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@

namespace Altinn.App.Core.Internal.Process.ServiceTasks;

internal interface IEformidlingServiceTask : IServiceTask { }

/// <summary>
/// Service task that sends eFormidling shipment, if EFormidling is enabled in config and EFormidling.SendAfterTaskId matches the current task.
/// </summary>
public class EformidlingServiceTask : IServiceTask
public class EformidlingServiceTask : IEformidlingServiceTask
{
private readonly ILogger<EformidlingServiceTask> _logger;
private readonly IAppMetadata _appMetadata;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

namespace Altinn.App.Core.Internal.Process.ServiceTasks;

internal interface IPdfServiceTask : IServiceTask { }

/// <summary>
/// Service task that generates PDFs for all connected datatypes that have the EnablePdfCreation flag set to true.
/// </summary>
public class PdfServiceTask : IServiceTask
public class PdfServiceTask : IPdfServiceTask
{
private readonly IAppMetadata _appMetadata;
private readonly IPdfService _pdfService;
Expand Down
94 changes: 94 additions & 0 deletions test/Altinn.App.Api.Tests/DITests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@

using Microsoft.Extensions.DependencyInjection;
using Xunit;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Configuration;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights;
using System.Diagnostics.Tracing;

namespace Altinn.App.Api.Tests;

public class DITests
{
private sealed record FakeWebHostEnvironment : IWebHostEnvironment, IHostingEnvironment
{
private string _env = "";

public string WebRootPath { get => new DirectoryInfo("./").FullName; set => throw new NotImplementedException(); }
public IFileProvider WebRootFileProvider { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public string ApplicationName { get => "test"; set => throw new NotImplementedException(); }
public IFileProvider ContentRootFileProvider { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public string ContentRootPath { get => new DirectoryInfo("./").FullName; set => throw new NotImplementedException(); }
public string EnvironmentName { get => _env; set => _env = value; }
}

private sealed class AppInsightsListener : EventListener
{
private readonly List<EventSource> _eventSources = [];
public readonly List<EventWrittenEventArgs> Events = [];

protected override void OnEventSourceCreated(EventSource eventSource)
{

if (eventSource.Name == "Microsoft-ApplicationInsights-AspNetCore")
{
_eventSources.Add(eventSource);
EnableEvents(eventSource, EventLevel.Verbose, EventKeywords.All);
}

base.OnEventSourceCreated(eventSource);
}

protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
if (eventData.EventSource.Name != "Microsoft-ApplicationInsights-AspNetCore")
{
return;
}

Events.Add(eventData);
base.OnEventWritten(eventData);
}

public override void Dispose()
{
foreach (var eventSource in _eventSources)
{
DisableEvents(eventSource);
}
base.Dispose();
}
}

[Fact]
public void AppInsights_Registers_Correctly()
{
using var listener = new AppInsightsListener();

var services = new ServiceCollection();
var env = new FakeWebHostEnvironment { EnvironmentName = "Development" };

services.AddSingleton<IWebHostEnvironment>(env);
services.AddSingleton<IHostingEnvironment>(env);

var config = new ConfigurationBuilder()
.AddInMemoryCollection([
new KeyValuePair<string, string?>("ApplicationInsights:InstrumentationKey", "test")
]).Build();

Extensions.ServiceCollectionExtensions.AddAltinnAppServices(services, config, env);

using var sp = services.BuildServiceProvider();

var telemetryConfig = sp.GetRequiredService<TelemetryConfiguration>();
Assert.NotNull(telemetryConfig);

var client = sp.GetRequiredService<TelemetryClient>();
Assert.NotNull(client);

EventLevel[] errorLevels = [EventLevel.Error, EventLevel.Critical];
Assert.Empty(listener.Events.Where(e => errorLevels.Contains(e.Level)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@ public class EndTaskEventHandlerTests
{
private readonly Mock<IProcessTaskDataLocker> _processTaskDataLocker = new();
private readonly Mock<IProcessTaskFinalizer> _processTaskFinisher = new();
private readonly Mock<IServiceTask> _pdfServiceTask = new();
private readonly Mock<IServiceTask> _eformidlingServiceTask = new();
private readonly Mock<IPdfServiceTask> _pdfServiceTask = new();
private readonly Mock<IEformidlingServiceTask> _eformidlingServiceTask = new();

private IServiceTask[] ServiceTasks => [
_pdfServiceTask.Object,
_eformidlingServiceTask.Object
];

private IEnumerable<IProcessTaskEnd> _processTaskEnds = new List<IProcessTaskEnd>();
private readonly ILogger<EndTaskEventHandler> _logger = new NullLogger<EndTaskEventHandler>();

Expand All @@ -25,8 +31,7 @@ public async Task Execute_handles_no_IProcessTaskAbandon_injected()
EndTaskEventHandler eteh = new EndTaskEventHandler(
_processTaskDataLocker.Object,
_processTaskFinisher.Object,
_pdfServiceTask.Object,
_eformidlingServiceTask.Object,
ServiceTasks,
_processTaskEnds,
_logger);
var instance = new Instance()
Expand Down Expand Up @@ -58,8 +63,7 @@ public async Task Execute_calls_all_added_implementations_of_IProcessTaskEnd()
EndTaskEventHandler eteh = new(
_processTaskDataLocker.Object,
_processTaskFinisher.Object,
_pdfServiceTask.Object,
_eformidlingServiceTask.Object,
ServiceTasks,
_processTaskEnds,
_logger);
var instance = new Instance()
Expand Down Expand Up @@ -92,8 +96,7 @@ public async Task Calls_unlock_if_pdf_fails()
EndTaskEventHandler eteh = new(
_processTaskDataLocker.Object,
_processTaskFinisher.Object,
_pdfServiceTask.Object,
_eformidlingServiceTask.Object,
ServiceTasks,
_processTaskEnds,
_logger);

Expand Down Expand Up @@ -124,4 +127,38 @@ public async Task Calls_unlock_if_pdf_fails()
// Make sure eFormidling service task is not called if PDF failed.
_eformidlingServiceTask.Verify(p => p.Execute(taskId, instance), Times.Never);
}

[Fact]
public void Throws_If_Missing_Pdf_ServiceTask()
{
IServiceTask[] serviceTasks = [
_eformidlingServiceTask.Object
];

var ex = Assert.Throws<InvalidOperationException>(() =>
new EndTaskEventHandler(
_processTaskDataLocker.Object,
_processTaskFinisher.Object,
serviceTasks,
_processTaskEnds,
_logger));
Assert.Equal("PdfServiceTask not found in serviceTasks", ex.Message);
}

[Fact]
public void Throws_If_Missing_Eformidling_ServiceTask()
{
IServiceTask[] serviceTasks = [
_pdfServiceTask.Object
];

var ex = Assert.Throws<InvalidOperationException>(() =>
new EndTaskEventHandler(
_processTaskDataLocker.Object,
_processTaskFinisher.Object,
serviceTasks,
_processTaskEnds,
_logger));
Assert.Equal("EformidlingServiceTask not found in serviceTasks", ex.Message);
}
}

0 comments on commit dc62cf8

Please sign in to comment.