Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue where AppInsights SDK doesn't load/configure correctly due to keyed services being registered in DI #594

Merged
merged 4 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
}
Loading