Skip to content
This repository has been archived by the owner on Dec 12, 2022. It is now read-only.

Commit

Permalink
Feat: Application Info in Agent Signature (via IAgentSignatureAugment…
Browse files Browse the repository at this point in the history
…er) (entitydb-io#37)

* feat: agent signature augmenter

* test: agent signature augmenter

* refactor: don't allow null

just use an empty dictionary

* chore: fix warning
  • Loading branch information
the-avid-engineer authored May 19, 2022
1 parent bd4b735 commit 44eb07a
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 12 deletions.
16 changes: 16 additions & 0 deletions src/EntityDb.Abstractions/Agents/IAgentSignatureAugmenter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Collections.Generic;

namespace EntityDb.Abstractions.Agents;

/// <summary>
/// Represents a type that can augment an agent signature by
/// providing additional, application-specific information.
/// </summary>
public interface IAgentSignatureAugmenter
{
/// <summary>
/// Returns a dictionary of application-specific information.
/// </summary>
/// <returns>A dictionary of application-specific information.</returns>
Dictionary<string, string> GetApplicationInfo();
}
15 changes: 13 additions & 2 deletions src/EntityDb.Mvc/Agents/HttpContextAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@
using EntityDb.Abstractions.ValueObjects;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using System.Collections.Generic;

namespace EntityDb.Mvc.Agents;

internal record HttpContextAgent(HttpContext HttpContext, IOptionsFactory<HttpContextAgentSignatureOptions> HttpContextAgentSignatureOptionsFactory) : IAgent
internal record HttpContextAgent
(
HttpContext HttpContext,
IOptionsFactory<HttpContextAgentSignatureOptions> HttpContextAgentSignatureOptionsFactory,
Dictionary<string, string> ApplicationInfo
) : IAgent
{
public TimeStamp GetTimeStamp()
{
Expand All @@ -14,6 +20,11 @@ public TimeStamp GetTimeStamp()

public object GetSignature(string signatureOptionsName)
{
return HttpContextAgentSignature.GetSnapshot(HttpContext, HttpContextAgentSignatureOptionsFactory.Create(signatureOptionsName));
return HttpContextAgentSignature.GetSnapshot
(
HttpContext,
HttpContextAgentSignatureOptionsFactory.Create(signatureOptionsName),
ApplicationInfo
);
}
}
17 changes: 15 additions & 2 deletions src/EntityDb.Mvc/Agents/HttpContextAgentAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,30 @@
using EntityDb.Common.Exceptions;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using System.Collections.Generic;

namespace EntityDb.Mvc.Agents;

internal sealed class HttpContextAgentAccessor : AgentAccessorBase
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IOptionsFactory<HttpContextAgentSignatureOptions> _httpContextAgentOptionsFactory;
private readonly IAgentSignatureAugmenter? _agentSignatureAugmenter;

public HttpContextAgentAccessor(IHttpContextAccessor httpContextAccessor, IOptionsFactory<HttpContextAgentSignatureOptions> httpContextAgentOptionsFactory)
public HttpContextAgentAccessor
(
IHttpContextAccessor httpContextAccessor,
IOptionsFactory<HttpContextAgentSignatureOptions> httpContextAgentOptionsFactory,
IAgentSignatureAugmenter? agentSignatureAugmenter = null
)
{
_httpContextAccessor = httpContextAccessor;
_httpContextAgentOptionsFactory = httpContextAgentOptionsFactory;
_agentSignatureAugmenter = agentSignatureAugmenter;
}

private static readonly Dictionary<string, string> DefaultApplicationInfo = new();

protected override IAgent CreateAgent()
{
var httpContext = _httpContextAccessor.HttpContext;
Expand All @@ -26,6 +36,9 @@ protected override IAgent CreateAgent()
throw new NoAgentException();
}

return new HttpContextAgent(httpContext, _httpContextAgentOptionsFactory);
var applicationInfo = _agentSignatureAugmenter?
.GetApplicationInfo() ?? DefaultApplicationInfo;

return new HttpContextAgent(httpContext, _httpContextAgentOptionsFactory, applicationInfo);
}
}
13 changes: 10 additions & 3 deletions src/EntityDb.Mvc/Agents/HttpContextAgentSignature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ int LocalPort
public sealed record Snapshot
(
RequestSnapshot Request,
ConnectionSnapshot Connection
ConnectionSnapshot Connection,
Dictionary<string, string> ApplicationInfo
);

private static NameValuesPairSnapshot[] GetNameValuesPairSnapshots(IEnumerable<KeyValuePair<string, StringValues>> dictionary, string[] redactedKeys, string redactedValue)
Expand Down Expand Up @@ -92,12 +93,18 @@ private static ConnectionSnapshot GetConnectionSnapshot(ConnectionInfo connectio
);
}

internal static Snapshot GetSnapshot(HttpContext httpContext, HttpContextAgentSignatureOptions httpContextAgentOptions)
internal static Snapshot GetSnapshot
(
HttpContext httpContext,
HttpContextAgentSignatureOptions httpContextAgentOptions,
Dictionary<string, string> applicationInfo
)
{
return new Snapshot
(
GetRequestSnapshot(httpContext.Request, httpContextAgentOptions),
GetConnectionSnapshot(httpContext.Connection)
GetConnectionSnapshot(httpContext.Connection),
applicationInfo
);
}
}
74 changes: 74 additions & 0 deletions test/EntityDb.Common.Tests/Agents/AgentAccessorTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using Shouldly;
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Moq;
using Xunit;

namespace EntityDb.Common.Tests.Agents;
Expand All @@ -19,6 +21,8 @@ protected AgentAccessorTestsBase(IServiceProvider startupServiceProvider) : base

protected abstract void ConfigureActiveAgentAccessor(IServiceCollection serviceCollection, TAgentAccessorConfiguration agentAccessorConfiguration);

protected abstract Dictionary<string, string>? GetApplicationInfo(object agentSignature);

protected abstract IEnumerable<TAgentAccessorConfiguration> GetAgentAccessorOptions();

[Fact]
Expand Down Expand Up @@ -109,4 +113,74 @@ public void GivenBackingServiceActive_WhenGettingAgentSignature_ThenReturnAgentS
agentSignature.ShouldNotBeNull();
}
}

[Fact]
public void GivenBackingServiceActiveAndNoSignatureAugmenter_WhenGettingApplicationInfo_ThenReturnEmptyApplicationInfo()
{
foreach (var agentAccessorConfiguration in GetAgentAccessorOptions())
{
// ARRANGE

using var serviceScope = CreateServiceScope(serviceCollection =>
{
ConfigureActiveAgentAccessor(serviceCollection, agentAccessorConfiguration);

serviceCollection.RemoveAll(typeof(IAgentSignatureAugmenter));
});

var agentAccessor = serviceScope.ServiceProvider
.GetRequiredService<IAgentAccessor>();

// ACT

var agentSignature = agentAccessor.GetAgent().GetSignature("").ShouldNotBeNull();

var applicationInfo = GetApplicationInfo(agentSignature);

// ASSERT

applicationInfo.ShouldBeEmpty();
}
}


[Fact]
public void GivenBackingServiceActiveAndHasSignatureAugmenter_WhenGettingApplicationInfo_ThenReturnExpectedApplicationInfo()
{
foreach (var agentAccessorConfiguration in GetAgentAccessorOptions())
{
// ARRANGE

var expectedApplicationInfo = new Dictionary<string, string>
{
["UserId"] = Guid.NewGuid().ToString()
};

var agentSignatureAugmenterMock = new Mock<IAgentSignatureAugmenter>(MockBehavior.Strict);

agentSignatureAugmenterMock
.Setup(x => x.GetApplicationInfo())
.Returns(expectedApplicationInfo);

using var serviceScope = CreateServiceScope(serviceCollection =>
{
ConfigureActiveAgentAccessor(serviceCollection, agentAccessorConfiguration);

serviceCollection.AddSingleton(agentSignatureAugmenterMock.Object);
});

var agentAccessor = serviceScope.ServiceProvider
.GetRequiredService<IAgentAccessor>();

// ACT

var agentSignature = agentAccessor.GetAgent().GetSignature("");

var actualApplicationInfo = GetApplicationInfo(agentSignature);

// ASSERT

actualApplicationInfo.ShouldBe(expectedApplicationInfo);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Moq;
using System;
using System.Collections.Generic;
using EntityDb.Mvc.Agents;

namespace EntityDb.Mvc.Tests.Agents;

Expand Down Expand Up @@ -58,4 +59,11 @@ protected override IEnumerable<HttpContextSeederOptions> GetAgentAccessorOptions
}
};
}

protected override Dictionary<string, string>? GetApplicationInfo(object agentSignature)
{
return agentSignature is not HttpContextAgentSignature.Snapshot httpContextAgentSignature
? null
: httpContextAgentSignature.ApplicationInfo;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void GivenNoRedactedHeaders_WhenHttpContextHasHeader_ThenAgentSignatureHa

// ACT

var (request, _) = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions);
var (request, _, _) = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions, default!);

// ASSERT

Expand Down Expand Up @@ -67,7 +67,7 @@ public void GivenRedactedHeader_WhenHttpContextContainsOnlyThatHeader_ThenAgentS

// ACT

var (request, _) = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions);
var (request, _, _) = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions, default!);

// ASSERT

Expand Down Expand Up @@ -100,7 +100,7 @@ public void GivenNoRedactedQueryStringParams_WhenHttpContextHasQueryStringParam_

// ACT

var (request, _) = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions);
var (request, _, _) = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions, default!);

// ASSERT

Expand Down Expand Up @@ -135,7 +135,7 @@ public void GivenRedactedQueryStringParam_WhenHttpContextContainsOnlyThatQuerySt

// ACT

var (request, _) = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions);
var (request, _, _) = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions, default!);

// ASSERT

Expand Down
2 changes: 1 addition & 1 deletion test/EntityDb.Mvc.Tests/Seeder/HttpContextSeeder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ private static ConnectionInfo CreateConnectionInfo(HttpContextSeederOptions http

connectionInfoMock
.SetupGet(info => info.Id)
.Returns(Id.NewId().ToString());
.Returns(Id.NewId().ToString()!);

var faker = new Faker();

Expand Down

0 comments on commit 44eb07a

Please sign in to comment.