Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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!');">
Comment thread
rossgrambo marked this conversation as resolved.
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 <script>document.write(getCookie("username"));</script></h1>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given this is a razor page, I'd recommend putting the username into the view data and using razor syntax to inject it into the DOM.

<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
23 changes: 23 additions & 0 deletions examples/EvaluationDataToApplicationInsights/Pages/Index.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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;

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

public IActionResult OnGet()
{
return Page();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<!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>
function getCookie(cname) {
let name = cname + "=";
let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}

function deleteCookie(name, path, domain) {
if (getCookie(name)) {
document.cookie = name + "=" +
((path) ? ";path=" + path : "") +
((domain) ? ";domain=" + domain : "") +
";expires=Thu, 01 Jan 1970 00:00:01 GMT";
}
}

appInsights.setAuthenticatedUserContext(getCookie("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">
Comment thread
rossgrambo marked this conversation as resolved.
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Home">Feature A</a>
</li>
</feature>
<feature name="FeatureB">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Home">Feature B</a>
</li>
</feature>
<li style="display: flex;">
<button onclick="document.cookie = ' username=' + Math.floor(Math.random() * 100000);deleteCookie('ai_user');deleteCookie('ai_session');window.location=window.location.origin;">
Comment thread
rossgrambo marked this conversation as resolved.
Outdated
Randomize User
</button>
</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