Skip to content
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
15 changes: 14 additions & 1 deletion Microsoft.FeatureManagement.sln
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TargetingConsoleApp", "exam
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RazorPages", "examples\RazorPages\RazorPages.csproj", "{BA29A1BB-81D5-4EB1-AF37-6ECF64AF27E2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.FeatureManagement.AspNetCore", "tests\Tests.FeatureManagement.AspNetCore\Tests.FeatureManagement.AspNetCore.csproj", "{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.FeatureManagement.AspNetCore", "tests\Tests.FeatureManagement.AspNetCore\Tests.FeatureManagement.AspNetCore.csproj", "{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EvaluationDataToApplicationInsights", "examples\EvaluationDataToApplicationInsights\EvaluationDataToApplicationInsights.csproj", "{FCB6725E-E150-411F-9363-DA9D5B938552}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.FeatureManagement.Telemetry.ApplicationInsights", "src\Microsoft.FeatureManagement.Telemetry.ApplicationInsights\Microsoft.FeatureManagement.Telemetry.ApplicationInsights.csproj", "{175590FB-3DEA-44A4-A50B-447AB2C84DB5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -61,6 +65,14 @@ Global
{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}.Release|Any CPU.Build.0 = Release|Any CPU
{FCB6725E-E150-411F-9363-DA9D5B938552}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FCB6725E-E150-411F-9363-DA9D5B938552}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FCB6725E-E150-411F-9363-DA9D5B938552}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FCB6725E-E150-411F-9363-DA9D5B938552}.Release|Any CPU.Build.0 = Release|Any CPU
{175590FB-3DEA-44A4-A50B-447AB2C84DB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{175590FB-3DEA-44A4-A50B-447AB2C84DB5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{175590FB-3DEA-44A4-A50B-447AB2C84DB5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{175590FB-3DEA-44A4-A50B-447AB2C84DB5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -72,6 +84,7 @@ Global
{6558C21E-CF20-4278-AA08-EB9D1DF29D66} = {FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}
{BA29A1BB-81D5-4EB1-AF37-6ECF64AF27E2} = {FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}
{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4} = {8ED6FFEE-4037-49A2-9709-BC519C104A90}
{FCB6725E-E150-411F-9363-DA9D5B938552} = {FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {84DA6C54-F140-4518-A1B4-E4CF42117FBD}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.FeatureManagement.AspNetCore\Microsoft.FeatureManagement.AspNetCore.csproj" />
<ProjectReference Include="..\..\src\Microsoft.FeatureManagement.Telemetry.ApplicationInsights\Microsoft.FeatureManagement.Telemetry.ApplicationInsights.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
using Microsoft.FeatureManagement.FeatureFilters;

namespace EvaluationDataToApplicationInsights
{
/// <summary>
/// Provides an implementation of <see cref="ITargetingContextAccessor"/> that creates a targeting context using info from the current HTTP request.
/// </summary>
public class HttpContextTargetingContextAccessor : ITargetingContextAccessor
{
private const string TargetingContextLookup = "HttpContextTargetingContextAccessor.TargetingContext";
private readonly IHttpContextAccessor _httpContextAccessor;

public HttpContextTargetingContextAccessor(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
}

public ValueTask<TargetingContext> GetContextAsync()
{
HttpContext httpContext = _httpContextAccessor.HttpContext;

//
// Try cache lookup
if (httpContext.Items.TryGetValue(TargetingContextLookup, out object value))
{
return new ValueTask<TargetingContext>((TargetingContext)value);
}

string username = httpContext.Request.Cookies["username"];

List<string> groups = new List<string>();

//
// Build targeting context based off user info
TargetingContext targetingContext = new TargetingContext
{
UserId = username,
Groups = groups
};

//
// Cache for subsequent lookup
httpContext.Items[TargetingContextLookup] = targetingContext;

return new ValueTask<TargetingContext>(targetingContext);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.Extensibility;

namespace EvaluationDataToApplicationInsights.Middlewares
{
public class MyTelemetryInitializer : ITelemetryInitializer
{
private readonly IHttpContextAccessor _httpContextAccessor;

public MyTelemetryInitializer(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
}

public void Initialize(ITelemetry telemetry)
{
HttpContext httpContext = _httpContextAccessor.HttpContext;

if (httpContext == null)
{
return;
}

string username = httpContext.Request.Cookies["username"];

if (username != null)
{
telemetry.Context.User.AuthenticatedUserId = username;
}
}
}
}
12 changes: 12 additions & 0 deletions examples/EvaluationDataToApplicationInsights/Pages/Checkout.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@page
@model CheckoutModel
@{
ViewData["Title"] = "Checkout";
}
<h1>@ViewData["Title"]</h1>

<p>Click Below To Check Out!</p>

<button id="checkout" name="checkout" onclick="appInsights.trackEvent({ name: 'checkout', properties: { 'success' : 'yes' } }); appInsights.trackMetric({ name: 'checkoutAmount', average: Math.floor(Math.random() * 100) }); alert('Checked Out!');">
Check Out
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace EvaluationDataToApplicationInsights.Pages
{
public class CheckoutModel : PageModel
{
private readonly ILogger<CheckoutModel> _logger;

public CheckoutModel(ILogger<CheckoutModel> logger)
{
_logger = logger;
}
}
}
26 changes: 26 additions & 0 deletions examples/EvaluationDataToApplicationInsights/Pages/Error.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@page
@model ErrorModel
@{
ViewData["Title"] = "Error";
}

<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}

<h3>Development Mode</h3>
<p>
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
27 changes: 27 additions & 0 deletions examples/EvaluationDataToApplicationInsights/Pages/Error.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Diagnostics;

namespace EvaluationDataToApplicationInsights.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }

public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

private readonly ILogger<ErrorModel> _logger;

public ErrorModel(ILogger<ErrorModel> logger)
{
_logger = logger;
}

public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}
10 changes: 10 additions & 0 deletions examples/EvaluationDataToApplicationInsights/Pages/Index.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}

<div class="text-center">
<h1 class="display-4">Welcome @Model.Username</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
31 changes: 31 additions & 0 deletions examples/EvaluationDataToApplicationInsights/Pages/Index.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.FeatureManagement;

namespace EvaluationDataToApplicationInsights.Pages
{
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly IFeatureManager _featureManager;
private readonly IHttpContextAccessor _httpContextAccessor;

public string Username { get; set; }

public IndexModel(ILogger<IndexModel> logger, IFeatureManager featureManager, IHttpContextAccessor httpContextAccessor)
{
_logger = logger;
_featureManager = featureManager;
_httpContextAccessor = httpContextAccessor;
}

public IActionResult OnGet()
{
Username = _httpContextAccessor.HttpContext.Request.Cookies["username"];

return Page();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@page
@model EvaluationDataToApplicationInsights.Pages.RandomizeUserModel
@{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System;

namespace EvaluationDataToApplicationInsights.Pages
{
public class RandomizeUserModel : PageModel
{
public IActionResult OnGet()
{
// Clear Application Insights cookies and generate new username
Response.Cookies.Delete("ai_user");
Response.Cookies.Delete("ai_session");
Response.Cookies.Append("username", Random.Shared.Next().ToString());

return RedirectToPage("/Index");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
@using System.Security.Claims
@using Microsoft.AspNetCore.Http

@inject IHttpContextAccessor httpContextAccessor
@{
string username = httpContextAccessor.HttpContext.Request.Cookies["username"];
}

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - EvaluationDataToApplicationInsights</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/EvaluationDataToApplicationInsights.styles.css" asp-append-version="true" />
@Html.Raw(JavaScriptSnippet.FullScript)

<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
<script>
appInsights.setAuthenticatedUserContext(@username);
</script>
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-page="/Index">EvaluationDataToApplicationInsights</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Checkout">Checkout</a>
</li>
<feature name="FeatureA">
<li class="nav-item">
<span class="nav-link text-muted">Feature A</span>
</li>
</feature>
<feature name="FeatureB">
<li class="nav-item">
<span class="nav-link text-muted">Feature B</span>
</li>
</feature>
<li style="display: flex;">
<form method="get" asp-page="/RandomizeUser">
<button style="height:100%" type="submit">Randomize User</button>
</form>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>

<footer class="border-top footer text-muted">
<div class="container">
&copy; 2023 - EvaluationDataToApplicationInsights - <a asp-area="" asp-page="/Checkout">Checkout</a>
</div>
</footer>

@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
Loading