Skip to content

Commit b0009cc

Browse files
author
Calin Lupas
authored
Merge pull request #34 from DevExcelerate/feature/add-telemetry-and-feature-flags
Add feature flags and telemetry
2 parents 067ce4e + 8be1fc1 commit b0009cc

8 files changed

Lines changed: 120 additions & 25 deletions

File tree

docs/main.md

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ This application is built using .NET Core. It supports multiple data stores, inc
2020
| `Logging:LogLevel:Default` | String | Specifies the default log level. |
2121
| `Logging:LogLevel:Microsoft.AspNetCore` | String | Log level for Microsoft.AspNetCore namespace. |
2222
| `AllowedHosts` | String | Comma-separated list of allowed hosts. |
23-
| `AllowedOrigins` | Array | List of allowed origins for CORS. Example: https://localhost:8000 |
23+
| `AllowedOrigins` | Array | List of allowed origins for CORS. Example: https://localhost:8000 |
2424
| `AppLocale` | String | Application locale, optional, defaults to "en" (English). |
2525
| `GitHub:AppId` | String | GitHub App ID. |
2626
| `GitHub:WebhookSecret` | String | Secret for GitHub webhooks. |
@@ -32,13 +32,37 @@ This application is built using .NET Core. It supports multiple data stores, inc
3232
| `GitHub:RepoRulesetsCustomPropertyName` | String | Custom property name for repository rulesets. |
3333
| `Azure:KeyVaultName` | String | Name of the Azure Key Vault. |
3434
| `Azure:KeyName` | String | Name of the key in Azure Key Vault. |
35+
| `Azure:AzureMonitor:EnableTelemetry` | String | Whether to enable telemetry or not. |
36+
| `Azure:AzureMonitor:UseEntraIdAuthentication` | String | When telemetry is enabled, whether to use entra for authentication vs Instrumentation Key. |
3537
| `DataStore:Type` | String | Type of data store (FileSystem, CosmosDb, SqlServer). |
3638
| `DataStore:FileSystem:FilePath` | String | File path for file system data store. |
3739
| `DataStore:CosmosDb:Database` | String | Database name for Cosmos DB. |
3840
| `DataStore:CosmosDb:ConnectionString` | String | Connection string for Cosmos DB. |
39-
| `DataStore:CosmosDb:GitHubWebhooksContainer` | String | Container for GitHub webhooks in Cosmos DB. |
41+
| `DataStore:CosmosDb:GitHubWebhooksContainer` | String | Container for GitHub webhooks in Cosmos DB. |
4042
| `DataStore:SqlServer:ConnectionString` | String | Connection string for SQL Server. |
4143

44+
### Feature flags
45+
46+
Follows the .NET FeatureManagement [schema](https://learn.microsoft.com/en-us/azure/azure-app-configuration/feature-management-dotnet-reference#net-feature-management-schema)
47+
48+
```json
49+
"feature_management": {
50+
"feature_flags": [
51+
{
52+
"id": "IssueAutoApprove",
53+
"display_name": "Auto Approve Issues",
54+
"description": "Whether to auto approve issues or let someone approve them",
55+
"enabled": true
56+
},
57+
{
58+
"id": "UseInventoryWithPullRequest",
59+
"display_name": "Use Pull Request for Inventory Metadata",
60+
"description": "Determines whether to use a pull request to populate the inventory metadata or not",
61+
"enabled": true
62+
}
63+
]
64+
}
65+
```
4266

4367
### Example
4468

@@ -67,7 +91,11 @@ This application is built using .NET Core. It supports multiple data stores, inc
6791
},
6892
"Azure": {
6993
"KeyVaultName": "",
70-
"KeyName": ""
94+
"KeyName": "",
95+
"AzureMonitor": {
96+
"EnableTelemetry": true,
97+
"UseEntraIdAuthentication": false
98+
}
7199
},
72100
"DataStore": {
73101
// FileSystem, CosmosDb, SqlServer
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace DevExcelerateApi.Core.Options;
2+
3+
public class AzureMonitorOptions
4+
{
5+
/// <summary>
6+
/// Gets the configuration section name for GitHub options.
7+
/// </summary>
8+
public static readonly string ConfigurationSectionName = "Azure:AzureMonitor";
9+
10+
/// <summary>
11+
/// Gets or sets whether to enable telemetry.
12+
/// </summary>
13+
public bool EnableTelemetry { get; set; }
14+
15+
/// <summary>
16+
/// Gets or sets whether to use Entra ID authentication instead of the instrumentation key.
17+
/// </summary>
18+
public bool UseEntraIdAuthentication { get; set; }
19+
}

src/DevExcelerateApi/Core/Options/GitHubOptions.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,6 @@ public class GitHubOptions
4646
[Required]
4747
public required string RepoPolicy { get; set; }
4848

49-
/// <summary>
50-
/// Flag to auto approve the issue opened
51-
/// </summary>
52-
[Required]
53-
public required bool IssueAutoApprove { get; set; }
54-
55-
/// <summary>
56-
/// Flag to save repository from issue event or use the inventory repository.
57-
/// </summary>
58-
[Required]
59-
public required bool UseInventoryWithPullRequest { get; set; }
60-
6149
/// <summary>
6250
/// The custom property name for the repository rulesets.
6351
/// </summary>

src/DevExcelerateApi/DevExcelerateApi.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
<ItemGroup>
1010
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.4.0" />
1111
<PackageReference Include="Azure.Identity" Version="1.13.2" />
12+
<PackageReference Include="Azure.Monitor.OpenTelemetry.AspNetCore" Version="1.2.0" />
1213
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.7.0" />
1314
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.49.0" />
15+
<PackageReference Include="Microsoft.FeatureManagement" Version="4.0.0" />
1416
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.9.0" />
1517
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
1618
<PackageReference Include="Octokit" Version="14.0.0" />

src/DevExcelerateApi/Program.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
using Azure.Monitor.OpenTelemetry.AspNetCore;
12
using DevExcelerateApi.Core.Extensions;
23
using DevExcelerateApi.Core.Options;
34
using DevExcelerateApi.Services;
45
using Microsoft.AspNetCore.Localization;
56
using Octokit.Webhooks.AspNetCore;
67
using System.Resources;
78
using System.Text.Json;
9+
using Azure.Core;
10+
using Azure.Identity;
11+
using Microsoft.FeatureManagement;
812

913
// see https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization/provide-resources?view=aspnetcore-9.0#culture-fallback-behavior
1014
[assembly: NeutralResourcesLanguage("en")]
@@ -39,6 +43,37 @@ public static async Task Main(string[] args)
3943
// Add named HTTP clients for IHttpClientFactory
4044
builder.Services.AddHttpClient();
4145

46+
var azureOptions = builder.Configuration.GetSection(Core.Options.AzureMonitorOptions.ConfigurationSectionName).Get<Core.Options.AzureMonitorOptions>();
47+
//If you set the connection string in multiple places, the environment variable will be prioritized in the following order:
48+
// Code
49+
// Environment variable (APPLICATIONINSIGHTS_CONNECTION_STRING)
50+
// Configuration file
51+
if (azureOptions!.EnableTelemetry)
52+
{
53+
builder.Services.AddOpenTelemetry().UseAzureMonitor(options =>
54+
{
55+
if (!azureOptions.UseEntraIdAuthentication)
56+
{
57+
return;
58+
}
59+
60+
TokenCredential azureCredentials = builder.Environment.IsDevelopment()
61+
// Only allow Developer style credentials in development
62+
? new ChainedTokenCredential(
63+
new VisualStudioCredential(),
64+
new VisualStudioCodeCredential(),
65+
new AzureCliCredential(),
66+
new AzureDeveloperCliCredential(),
67+
new AzurePowerShellCredential())
68+
// Only allow managed identity credential in production
69+
: new ManagedIdentityCredential();
70+
71+
options.Credential = azureCredentials;
72+
});
73+
}
74+
75+
builder.Services.AddFeatureManagement();
76+
4277
// Application Build & Run
4378

4479
// Configure middleware and endpoints

src/DevExcelerateApi/Properties/launchSettings.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
"launchBrowser": false,
88
"applicationUrl": "http://localhost:5131",
99
"environmentVariables": {
10-
"ASPNETCORE_ENVIRONMENT": "Development"
10+
"ASPNETCORE_ENVIRONMENT": "Development",
11+
"APPLICATIONINSIGHTS_CONNECTION_STRING": ""
1112
}
1213
},
1314
"https": {
@@ -16,7 +17,8 @@
1617
"launchBrowser": false,
1718
"applicationUrl": "https://localhost:7135;http://localhost:5131",
1819
"environmentVariables": {
19-
"ASPNETCORE_ENVIRONMENT": "Development"
20+
"ASPNETCORE_ENVIRONMENT": "Development",
21+
"APPLICATIONINSIGHTS_CONNECTION_STRING": ""
2022
}
2123
}
2224
}

src/DevExcelerateApi/Services/DevExIssuesEventProcessorService.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using DevExcelerateApi.Data;
44
using DevExcelerateApi.Models;
55
using Microsoft.Extensions.Options;
6+
using Microsoft.FeatureManagement;
67
using Octokit.Webhooks;
78
using Octokit.Webhooks.Events;
89
using Octokit.Webhooks.Events.IssueComment;
@@ -19,7 +20,8 @@ public class DevExIssuesEventProcessorService(
1920
IIssueService issueService,
2021
GitHubWebhookRepository gitHubWebhookRepository,
2122
IOptions<GitHubOptions> gitHubOptions,
22-
ILocalizationService localizationService)
23+
ILocalizationService localizationService,
24+
IVariantFeatureManager featureManager)
2325
: IDevExIssuesEventProcessorService
2426
{
2527
private readonly ILogger<DevExIssuesEventProcessorService> _logger = logger;
@@ -29,6 +31,7 @@ public class DevExIssuesEventProcessorService(
2931
private readonly GitHubWebhookRepository _gitHubWebhookRepository = gitHubWebhookRepository;
3032
private readonly GitHubOptions _gitHubOptions = gitHubOptions.Value;
3133
private readonly ILocalizationService _localizationService = localizationService;
34+
private readonly IVariantFeatureManager _featureManager = featureManager;
3235

3336
public async Task SaveAndProcessIssueEventAsync(WebhookHeaders headers, IssuesEvent issuesEvent, IssuesAction action)
3437
{
@@ -88,7 +91,7 @@ private async Task ValidateIssueApproval(IssuesEvent issuesEvent)
8891
// Opened
8992
if ((issuesEvent?.Issue?.Labels?.Any(i => i.Name.IsIssueOpened())).GetValueOrDefault())
9093
{
91-
if (_gitHubOptions.IssueAutoApprove)
94+
if (await _featureManager.IsEnabledAsync("IssueAutoApprove"))
9295
{
9396
// TODO: add more conditions for auto-approval
9497

@@ -130,7 +133,7 @@ private async Task ValidateIssue(IssuesEvent issuesEvent)
130133
}
131134
else if ((issuesEvent?.Issue?.Labels?.Any(i => i.Name.IsIssueValidated())).GetValueOrDefault())
132135
{
133-
if (_gitHubOptions.UseInventoryWithPullRequest)
136+
if (await _featureManager.IsEnabledAsync("UseInventoryWithPullRequest"))
134137
{
135138
// IF validated, create a pull request
136139
await _repositoryService.ValidateCreateRepository(issuesEvent!, createPullRequest: true);
@@ -186,12 +189,12 @@ private async Task ValidateIssueCommentApproval(string data, IssueCommentEvent i
186189
)
187190
)
188191
{
189-
await _issueService.ChangeIssueLabel([TicketStatus.APPROVED.ToString()], issueCommentEvent!);
192+
await _issueService.ChangeIssueLabel([nameof(TicketStatus.APPROVED)], issueCommentEvent!);
190193
}
191194
// Opened -> Rejected
192195
else if ((data?.IsIssueRejected()).GetValueOrDefault() && !(issueCommentEvent?.Issue?.Labels?.Any(i => i.Name.IsIssueRejected())).GetValueOrDefault())
193196
{
194-
await _issueService.ChangeIssueLabel([TicketStatus.REJECTED.ToString()], issueCommentEvent!);
197+
await _issueService.ChangeIssueLabel([nameof(TicketStatus.REJECTED)], issueCommentEvent!);
195198
}
196199
}
197200
}

src/DevExcelerateApi/appsettings.json

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
"RepoOnboarding": "",
1717
"RepoInventory": "",
1818
"RepoPolicy": "",
19-
"IssueAutoApprove": "true",
20-
"UseInventoryWithPullRequest": "true",
2119
"RepoRulesetsCustomPropertyName": ""
2220
},
2321
"Azure": {
2422
"KeyVaultName": "",
25-
"KeyName": ""
23+
"KeyName": "",
24+
"AzureMonitor": {
25+
"EnableTelemetry": false,
26+
"UseEntraIdAuthentication": false
27+
}
2628
},
2729
"DataStore": {
2830
// FileSystem, CosmosDb, SqlServer
@@ -40,5 +42,21 @@
4042
"SqlServer": {
4143
"ConnectionString": "" // dotnet user-secrets set "DataStore:SqlServer:ConnectionString" "MY_SQLSERVER_CONNECTION_STRING"
4244
}
45+
},
46+
"feature_management": {
47+
"feature_flags": [
48+
{
49+
"id": "IssueAutoApprove",
50+
"display_name": "Auto Approve Issues",
51+
"description": "Whether to auto approve issues or let someone approve them",
52+
"enabled": true
53+
},
54+
{
55+
"id": "UseInventoryWithPullRequest",
56+
"display_name": "Use Pull Request for Inventory Metadata",
57+
"description": "Determines whether to use a pull request to populate the inventory metadata or not",
58+
"enabled": true
59+
}
60+
]
4361
}
4462
}

0 commit comments

Comments
 (0)