diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 806161f8..556f4ae3 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -9,6 +9,10 @@ updates:
open-pull-requests-limit: 10
assignees:
- "samsmithnz"
+ groups:
+ tests:
+ patterns: ["*"]
+ update-types: ["minor", "patch"]
- package-ecosystem: nuget
directory: "/src/DevOpsMetrics.Web"
schedule:
@@ -18,6 +22,10 @@ updates:
open-pull-requests-limit: 10
assignees:
- "samsmithnz"
+ groups:
+ web:
+ patterns: ["*"]
+ update-types: ["minor", "patch"]
- package-ecosystem: nuget
directory: "/src/DevOpsMetrics.FunctionalTests"
schedule:
@@ -25,15 +33,10 @@ updates:
time: "06:00"
timezone: America/New_York
open-pull-requests-limit: 10
- assignees:
- - "samsmithnz"
-- package-ecosystem: npm
- directory: /src/ProbotMetrics
- schedule:
- interval: daily
- time: "06:00"
- timezone: America/New_York
- open-pull-requests-limit: 10
+ groups:
+ functionalTests:
+ patterns: ["*"]
+ update-types: ["minor", "patch"]
assignees:
- "samsmithnz"
# Maintain dependencies for GitHub Actions
@@ -41,5 +44,12 @@ updates:
directory: "/"
schedule:
interval: "daily"
+ time: "06:00"
+ timezone: America/New_York
+ open-pull-requests-limit: 10
assignees:
- "samsmithnz"
+ groups:
+ actions:
+ patterns: ["*"]
+ update-types: ["minor", "patch"]
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 53ff6b38..15bd26ea 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -38,11 +38,11 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -51,14 +51,14 @@ jobs:
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Setup .NET Core
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4.0.1
with:
- dotnet-version: 7.0.x
+ dotnet-version: 10.0.x
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ uses: github/codeql-action/autobuild@v3
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -72,4 +72,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@v3
diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml
index 17909567..ef732f66 100644
--- a/.github/workflows/dotnetcore.yml
+++ b/.github/workflows/dotnetcore.yml
@@ -11,12 +11,29 @@ jobs:
build:
runs-on: windows-latest
+ outputs: # https://stackoverflow.com/questions/59175332/using-output-from-a-previous-job-in-a-new-one-in-a-github-action
+ Version: ${{ steps.gitversion.outputs.SemVer }}
+ CommitsSinceVersionSource: ${{ steps.gitversion.outputs.CommitsSinceVersionSource }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 #fetch-depth is needed for GitVersion
+ #Install and calculate the new version with GitVersion
+ - name: Install GitVersion
+ uses: gittools/actions/gitversion/setup@v1.2.0
+ with:
+ versionSpec: 5.x
+ - name: Determine Version
+ uses: gittools/actions/gitversion/execute@v1.2.0
+ id: gitversion # step id used as reference for output values
+ - name: Display GitVersion outputs
+ run: |
+ echo "Version: ${{ steps.gitversion.outputs.SemVer }}"
+ echo "CommitsSinceVersionSource: ${{ steps.gitversion.outputs.CommitsSinceVersionSource }}"
- name: Setup .NET Core
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4.0.1
with:
- dotnet-version: 7.0.x
+ dotnet-version: 10.0.x
#Publish dotnet objects
- name: .NET Publish Web Service
@@ -27,33 +44,33 @@ jobs:
- name: .NET Publish functional tests
run: dotnet publish src/DevOpsMetrics.FunctionalTests/DevOpsMetrics.FunctionalTests.csproj --configuration Release --output ${{ github.workspace }}/functionalTests
- name: Copy chromedriver for functional test
- run: copy "src/DevOpsMetrics.FunctionalTests/bin/Release/net7.0/chromedriver.exe" "${{ github.workspace }}/functionalTests"
+ run: copy "src/DevOpsMetrics.FunctionalTests/bin/Release/net10.0/chromedriver.exe" "${{ github.workspace }}/functionalTests"
shell: powershell
- name: DotNet restore functional tests to get correct NewtonSoft version
run: dotnet restore src/DevOpsMetrics.FunctionalTests/DevOpsMetrics.FunctionalTests.csproj
- name: Copy new NewtonSoft version for functional test
- run: copy "src/DevOpsMetrics.FunctionalTests/bin/Release/net7.0/Newtonsoft.Json.dll" "${{ github.workspace }}/functionalTests"
+ run: copy "src/DevOpsMetrics.FunctionalTests/bin/Release/net10.0/Newtonsoft.Json.dll" "${{ github.workspace }}/functionalTests"
shell: powershell
#Publish build artifacts to GitHub
- name: Upload web service build artifacts back to GitHub
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: serviceapp
path: ${{ github.workspace }}/webservice
- name: Upload website build artifacts back to GitHub
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: webapp
path: ${{ github.workspace }}/web
- name: Upload functional test build artifacts back to GitHub
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: functionaltests
path: ${{ github.workspace }}/functionalTests
- name: Upload probot build artifacts back to GitHub
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: probot
path: src/ProbotMetrics
@@ -64,11 +81,11 @@ jobs:
permissions:
actions: read
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Setup .NET Core
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4.0.1
with:
- dotnet-version: 7.0.x
+ dotnet-version: 10.0.x
#Run automated .NET tests
- name: Variable Substitution appsettings file for tests
uses: microsoft/variable-substitution@v1
@@ -84,31 +101,30 @@ jobs:
- name: build test project
run: dotnet build src/DevOpsMetrics.Tests/DevOpsMetrics.Tests.csproj --configuration Debug
- name: Run automated unit and integration tests
- run: dotnet test src/DevOpsMetrics.Tests/DevOpsMetrics.Tests.csproj --no-build --configuration Debug --logger trx /p:CollectCoverage=true /p:CoverletOutput=TestResults/ /p:CoverletOutputFormat=lcov --settings:./src/DevOpsMetrics.Tests/CodeCoverage.runsettings
+ run: dotnet test src/DevOpsMetrics.Tests/DevOpsMetrics.Tests.csproj --no-build --configuration Debug --logger trx -e:CollectCoverage=true -e:CoverletOutput=TestResults/ -e:CoverletOutputFormat=lcov --settings:./src/DevOpsMetrics.Tests/CodeCoverage.runsettings
- name: Publish coverage report to coveralls.io
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: src/DevOpsMetrics.Tests/TestResults/coverage.info
- continue-on-error: true
buildFunction:
runs-on: windows-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Setup .NET Core
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4.0.1
with:
- dotnet-version: 7.0.x
+ dotnet-version: 10.0.x #Not a typo, the function is locked to a LTS version of .NET
- name: .NET Publish Function
run: dotnet publish src/DevOpsMetrics.Function/DevOpsMetrics.Function.csproj --configuration Debug --output ${{ github.workspace }}/function
- name: Upload function build artifacts back to GitHub
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: functionapp
path: ${{ github.workspace }}/function
-
+
#Disabling PROBOT for 2021 reboot to focus on website
#Deploy the probot artifacts to Azure
@@ -120,7 +136,7 @@ jobs:
# steps:
# # Login with the secret SP details
# - name: Log into Azure
-# uses: azure/login@v1
+# uses: azure/login@v2
# with:
# creds: ${{ secrets.AZURE_SP_PROBOT }}
# - name: Download webapp artifact
@@ -128,7 +144,7 @@ jobs:
# with:
# name: probot
# - name: Deploy probot to Azure WebApp
-# uses: Azure/webapps-deploy@v2.2.3
+# uses: Azure/webapps-deploy@v2.2.11
# with:
# app-name: devops-prod-eu-probot
# package: probot
@@ -143,38 +159,39 @@ jobs:
- build
- test
- buildFunction
+ if: github.ref == 'refs/heads/main'
steps:
# Login with the secret SP details
- name: Log into Azure
- uses: azure/login@v1
+ uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_SP }}
#Download the artifacts from GitHub
- name: Download serviceapp artifact
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
name: serviceapp
path: serviceapp
- name: Download webapp artifact
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
name: webapp
path: webapp
- name: Download webapp artifact
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
name: functionapp
path: functionapp
- name: Download functionaltests artifact
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
name: functionaltests
path: functionaltests
#Deploy service, website, and function to Azure
- name: Deploy web service to Azure WebApp
- uses: Azure/webapps-deploy@v2.2.3
+ uses: Azure/webapps-deploy@v3
with:
app-name: devops-prod-eu-service
package: serviceapp
@@ -183,7 +200,7 @@ jobs:
run: az webapp config appsettings set --name "devops-prod-eu-service" --resource-group "devopsmetrics" --settings "AppSettings:AzureDevOpsPatToken=${{ secrets.AzureDevOpsPATToken }}" "AppSettings:GitHubClientId=${{ secrets.GitHubClientId }}" "AppSettings:GitHubClientSecret=${{ secrets.GitHubClientSecret }}" "AppSettings:AzureStorageAccountConfigurationString=${{ secrets.AzureStorageConnectionString }}" "AppSettings:KeyVaultClientId=${{ secrets.KeyVaultClientId }}" "AppSettings:KeyVaultClientSecret=${{ secrets.KeyVaultClientSecret }}" #--slot "staging"
- name: Deploy website to Azure WebApp
- uses: Azure/webapps-deploy@v2.2.3
+ uses: Azure/webapps-deploy@v3
with:
app-name: devops-prod-eu-web
package: webapp
@@ -207,14 +224,27 @@ jobs:
# deploy function and settings
- name: Deploy function to Azure WebApp
- uses: Azure/webapps-deploy@v2.2.3
+ uses: Azure/webapps-deploy@v3
with:
app-name: devops-prod-eu-function
package: functionapp
- name: Deploy function app settings
run: az webapp config appsettings set --name "devops-prod-eu-function" --resource-group "devopsmetrics" --settings "AppSettings:AzureDevOpsPatToken=${{ secrets.AzureDevOpsPATToken }}" "AppSettings:GitHubClientId=${{ secrets.GitHubClientId }}" "AppSettings:GitHubClientSecret=${{ secrets.GitHubClientSecret }}" "AppSettings:AzureStorageAccountConfigurationString=${{ secrets.AzureStorageConnectionString }}" "AppSettings:WebServiceURL=https://devops-prod-eu-service.azurewebsites.net/" "AppSettings:KeyVaultClientId=${{ secrets.KeyVaultClientId }}" "AppSettings:KeyVaultClientSecret=${{ secrets.KeyVaultClientSecret }}" "AppSettings:KeyVaultURL=https://devops-prod-eu-vault.vault.azure.net/" "AzureStorageAccountContainerAzureDevOpsBuilds=DevOpsAzureDevOpsBuilds" "AppSettings:AzureStorageAccountContainerAzureDevOpsPRs=DevOpsAzureDevOpsPRs" "AppSettings:AzureStorageAccountContainerAzureDevOpsPRCommits=DevOpsAzureDevOpsPRCommits" "AppSettings:AzureStorageAccountContainerAzureDevOpsSettings=DevOpsAzureDevOpsSettings" "AppSettings:AzureStorageAccountContainerGitHubRuns=DevOpsGitHubRuns" "AppSettings:AzureStorageAccountContainerGitHubPRs=DevOpsGitHubPRs" "AppSettings:AzureStorageAccountContainerGitHubPRCommits=DevOpsGitHubPRCommits" "AppSettings:AzureStorageAccountContainerGitHubSettings=DevOpsGitHubSettings" "AppSettings:AzureStorageAccountContainerMTTR=DevOpsMTTR" "AppSettings:AzureStorageAccountContainerChangeFailureRate=DevOpsChangeFailureRate" "AppSettings:AzureStorageAccountContainerTableLog=DevOpsLog"
+ - name: Display GitVersion outputs
+ run: |
+ echo "Version: ${{ needs.build.outputs.Version }}"
+ echo "CommitsSinceVersionSource: ${{ needs.build.outputs.CommitsSinceVersionSource }}"
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1
+ if: github.ref == 'refs/heads/main' && needs.build.outputs.CommitsSinceVersionSource > 0 #Only create a release if there has been a commit/version change
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
+ with:
+ tag_name: "v${{ needs.build.outputs.Version }}"
+ release_name: "v${{ needs.build.outputs.Version }}"
-
+
swapDeploymentSlots:
runs-on: ubuntu-latest # Note, Azure CLI requires a Linux runner...
needs: [build, deployToStagingSlots]
@@ -223,18 +253,18 @@ jobs:
steps:
# Login with the secret SP details
- name: Log into Azure
- uses: azure/login@v1
+ uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_SP }}
#Swap staging slots with prod
- name: Swap web service staging slot to production
- uses: Azure/cli@v1.0.7
+ uses: Azure/cli@v2.0.0
with:
inlineScript: az webapp deployment slot swap --name "devops-prod-eu-service" --resource-group "devopsmetrics" --slot staging --target-slot production
- name: Deploy web service app settings
run: az webapp config appsettings set --name "devops-prod-eu-service" --resource-group "devopsmetrics" --settings "AppSettings:AzureDevOpsPatToken=${{ secrets.AzureDevOpsPATToken }}" "AppSettings:GitHubClientId=${{ secrets.GitHubClientId }}" "AppSettings:GitHubClientSecret=${{ secrets.GitHubClientSecret }}" "AppSettings:AzureStorageAccountConfigurationString=${{ secrets.AzureStorageConnectionString }}"
- name: Swap web site staging slot to production
- uses: Azure/cli@v1.0.7
+ uses: Azure/cli@v2.0.0
with:
inlineScript: az webapp deployment slot swap --name "devops-prod-eu-web" --resource-group "devopsmetrics" --slot staging --target-slot production
- name: Deploy website app settings
@@ -255,9 +285,9 @@ jobs:
# extensionVersion: "0.0.0"
# #if: github.ref == 'refs/heads/main'
# steps:
-# - uses: actions/checkout@v3
+# - uses: actions/checkout@v4
# - name: Setup .NET Core
-# uses: actions/setup-dotnet@v3
+# uses: actions/setup-dotnet@v4.0.1
# with:
# dotnet-version: 3.1.101
# - name: .NET core Publish Azure DevOps Extension
@@ -274,7 +304,7 @@ jobs:
# dir
# Write-Host "New env variable " ${{ env.extensionVersion }}
# - name: Upload Azure DevOps extension build artifacts back to GitHub
-# uses: actions/upload-artifact@v3
+# uses: actions/upload-artifact@v4
# with:
# name: azuredevopsextensionpackage
# path: src\DevOpsMetrics.AzureDevOpsExtension\bin\Release\netcoreapp3.1\publish\wwwroot\SamSmithNZ.high-performing-devops-metrics-${{ env.extensionVersion }}.vsix
diff --git a/GitVersion.yml b/GitVersion.yml
index 74d295e6..a87cc81a 100644
--- a/GitVersion.yml
+++ b/GitVersion.yml
@@ -1 +1 @@
-next-version: 1.3.0
+next-version: 1.11.0
diff --git a/README.md b/README.md
index 91fc1f76..346ad195 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,12 @@
[](https://GitHub.com/samsmithnz/DevOpsMetrics/actions?query=workflow%3ACI%2FCD)
[](https://coveralls.io/github/samsmithnz/DevOpsMetrics?branch=main)
+[](https://github.com/samsmithnz/DevOpsMetrics/releases)
-**Why should we care about DevOps Metrics and what are they?** All engineering, including software, needs metrics to track performance, but many metrics when measured individually, can be 'gamed', or don't encourage the right behaviors or incentives. This has been an issue with metrics for many years. The [DORA metrics](https://services.google.com/fh/files/misc/state-of-devops-2019.pdf) are a step in the right direction, combining several metrics that encourage the behaviors and incentives - and hence that encourage DevOps teams to perform at a high level of performance.
+
+**Why should we care about DevOps Metrics and what are they?** All engineering, including software, needs metrics to track performance, but many metrics when measured individually, can be 'gamed', or don't encourage the right behaviors or incentives. This has been an issue with metrics for many years. The [DORA metrics](https://services.google.com/fh/files/misc/state-of-devops-2019.pdf) are a step in the right direction, combining several metrics that encourage the behaviors and incentives - and hence that encourage DevOps teams to perform at a high level of performance. DORA metrics aren't perfect, but are still the best we have available today.
+- A [demo website displaying these metrics can be viewed here](https://devops-prod-eu-web.azurewebsites.net/).
+- Insights I've noted about implementing DORA DevOps metrics can be found in a [blog post here](https://samlearnsazure.blog/2020/04/30/high-performing-devops-metrics/)
This project is focused on helping you collect and analyze four key high performing DevOps metrics from GitHub and Azure DevOps. [DORA's "State of DevOps" research](https://cloud.google.com/blog/products/devops-sre/announcing-dora-2021-accelerate-state-of-devops-report) and [Accelerate](https://www.amazon.com/Accelerate-Software-Performing-Technology-Organizations/dp/1942788339) highlighted four driving indicators of high performing DevOps teams. While these four metrics are widely used in DevOps discussion, it's challenging to implement and capture all of the metrics.
@@ -12,13 +16,11 @@ This project is focused on helping you collect and analyze four key high perform
- **Mean time to restore (MTTR): How quickly restoration of production occurs in an outage or degradation.** When there is a degradation, how quickly can the system auto-heal itself, scale to handle increased load, and/or This one is contraversal, as it's challenging to compare different events that cause degradation.
- **Change failure rate: After a production deployment, was it successful? Or was a fix or rollback required after the fact?** How often is a change we made 'successful'? This ties in well with deployment frequency and lead time for changes, but is challenging to measure - as it requires a signoff off of success. Not just that the code deployed correctly, but that there weren't adverse effects or degradation of the deployment to the system
-
-(Chart from [page 9 of state of DevOps 2021 report](https://services.google.com/fh/files/misc/state-of-devops-2021.pdf))
-A [demo website displaying the metrics can be viewed here](https://devops-prod-eu-web.azurewebsites.net/).
-More information about high performing DevOps metrics can be found in a [blog post here](https://samlearnsazure.blog/2020/04/30/high-performing-devops-metrics/)
+
+[^1]
## The current solution:
-**We currently have all four of the metrics implemented and undergoing a pilot. There is a Probot for GitHub. (The Azure DevOps widget is on hold to focus on GitHub).**
+**We currently have all four of the metrics implemented and undergoing a pilot. (The Azure DevOps widget is currently not planned - but is possible if someone wants to build it!).**
- **Deployment Frequency**, in both Azure DevOps and GitHub:
- How does it work? We look at the number of successful pipeline runs.
@@ -60,12 +62,11 @@ More information about high performing DevOps metrics can be found in a [blog po

# Architecture
-Developed in .NET 7. A GitHub action runs the CI/CD process.
+Developed in .NET 10. A GitHub action runs the CI/CD process.
Currently the CI/CD process:
1. Builds the code
2. Runs the unit tests
-3. ~~Deploys the Probot code to a Azure web app (http://devops-prod-eu-probot.azurewebsites.net/)~~ (Currently disabled)
3. Deploys the webservice to a Azure web app (https://devops-prod-eu-service.azurewebsites.net)
4. Deploys the demo website to a Azure web app (https://devops-prod-eu-web.azurewebsites.net)
4. Deploys the function website to a Azure function
@@ -75,8 +76,8 @@ Dependabot runs daily to check for dependency upgrades.

## Badges
-The API can generate a URL for static badges, but more work is needed. Some current samples are shown below:
-[](https://img.shields.io/badge/Deployment%20frequency-Elite-brightgreen) [](https://img.shields.io/badge/Lead%20time%20for%20changes-High-green) [](https://img.shields.io/badge/Time%20to%20restore%20service-Medium-orange) [](https://img.shields.io/badge/Change%20failure%20rate-Low-red)
+The API can generate a URL for static badges, some samples are shown below:
+[](https://img.shields.io/badge/Lead%20time%20for%20changes-High-green) [](https://img.shields.io/badge/Time%20to%20restore%20service-Medium-orange) [](https://img.shields.io/badge/Change%20failure%20rate-Low-red)
# Setup
@@ -91,7 +92,6 @@ The API can generate a URL for static badges, but more work is needed. Some curr
# What's next?
- Upgrades to packaging and setup (in progress)
- Upgrades to store data in CosmosDB (currently in Azure storage)
-- Reviewing the current GitHub probot approach, to find a better target than issues (perhaps a metrics readme.md file?)
- Support for more scenarios, releases, etc
- ~~Azure DevOps marketplace integrations, so you can see the changes real time on your project/repo.~~ (lower priority to focus on GitHub)
@@ -99,3 +99,5 @@ The API can generate a URL for static badges, but more work is needed. Some curr
- GitHub API: https://developer.GitHub.com/v3/actions/workflow-runs/
- Azure DevOps API: https://docs.microsoft.com/en-us/rest/api/azure/devops/build/builds/list?view=azure-devops-rest-5.1
+
+[^1]: Chart from [page 11 of state of DevOps 2022 report](https://cloud.google.com/devops/state-of-devops)
diff --git a/src/DevOpsMetrics.Cmd/DevOpsMetrics.Cmd.csproj b/src/DevOpsMetrics.Cmd/DevOpsMetrics.Cmd.csproj
new file mode 100644
index 00000000..71e32af7
--- /dev/null
+++ b/src/DevOpsMetrics.Cmd/DevOpsMetrics.Cmd.csproj
@@ -0,0 +1,34 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+ dc96f8ff-2de5-4ed3-b124-89a134688b66
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/DevOpsMetrics.Cmd/Program.cs b/src/DevOpsMetrics.Cmd/Program.cs
new file mode 100644
index 00000000..924aebc7
--- /dev/null
+++ b/src/DevOpsMetrics.Cmd/Program.cs
@@ -0,0 +1,99 @@
+using Azure.Identity;
+using DevOpsMetrics.Core.DataAccess.TableStorage;
+using DevOpsMetrics.Core.Models.AzureDevOps;
+using DevOpsMetrics.Core.Models.Common;
+using DevOpsMetrics.Core.Models.GitHub;
+using DevOpsMetrics.Service;
+using DevOpsMetrics.Service.Controllers;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using Azure.Core;
+using Azure.Identity;
+
+namespace DevOpsMetrics.Cmd
+{
+ internal class Program
+ {
+ private static string ProjectId = "DeveloperMetrics_DevOpsMetrics";
+
+ static async Task Main()
+ {
+ DateTime startTime = DateTime.Now;
+ Console.WriteLine($"C# Timer trigger function UpdateStorageTables started at: {startTime}");
+
+ //Load settings
+ IConfigurationBuilder? builder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json", optional: false)
+ .AddUserSecrets(true);
+ IConfigurationRoot Configuration = builder.Build();
+ ILogger log = new Logger(new LoggerFactory());
+
+ string? keyVaultURL = Configuration["AppSettings:KeyVaultURL"];
+ string? keyVaultId = Configuration["AppSettings:KeyVaultClientId"];
+ string? keyVaultSecret = Configuration["AppSettings:KeyVaultClientSecret"];
+ string? tenantId = Configuration["AppSettings:TenantId"];
+ //AzureServiceTokenProvider azureServiceTokenProvider = new();
+ //KeyVaultClient keyVaultClient = new(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
+ //builder.AddAzureKeyVault(keyVaultURL, keyVaultId, keyVaultSecret);
+ if (keyVaultURL != null && keyVaultId != null && keyVaultSecret != null && tenantId != null)
+ {
+ TokenCredential tokenCredential = new ClientSecretCredential(tenantId, keyVaultId, keyVaultSecret);
+ builder.AddAzureKeyVault(new(keyVaultURL), tokenCredential);
+ }
+ else
+ {
+ throw new System.Exception("Missing configuration for Azure Key Vault");
+ }
+ Configuration = builder.Build();
+ ServiceApiClient serviceApiClient = new(Configuration);
+
+ //Get settings
+ string clientId = Configuration["AppSettings:GitHubClientId"];
+ string clientSecret = Configuration["AppSettings:GitHubClientSecret"];
+ AzureTableStorageDA azureTableStorageDA = new();
+ SettingsController settingsController = new(Configuration, new AzureTableStorageDA());
+ DORASummaryController doraSummaryController = new(Configuration);
+ List azSettings = await settingsController.GetAzureDevOpsSettings();
+ List ghSettings = await settingsController.GetGitHubSettings();
+ TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
+
+ //Loop through each setting to update the runs, pull requests and pull request commits
+ int numberOfDays = 30;
+ int maxNumberOfItems = 20;
+ int totalResults = 0;
+ foreach (AzureDevOpsSettings azSetting in azSettings)
+ {
+ if (ProjectId == azSetting.RowKey)
+ {
+ log.LogInformation($"Processing Azure DevOps organization {azSetting.Organization}, project {azSetting.Project}");
+ ProcessingResult ghResult = await doraSummaryController.UpdateDORASummaryItem(
+ azSetting.Organization, azSetting.Project, azSetting.Repository,
+ azSetting.Branch, azSetting.BuildName, azSetting.BuildId,
+ azSetting.ProductionResourceGroup,
+ numberOfDays, maxNumberOfItems,
+ null, true, false);
+ totalResults = ghResult.TotalResults;
+ }
+ }
+
+ foreach (GitHubSettings ghSetting in ghSettings)
+ {
+ if (ProjectId == ghSetting.RowKey)
+ {
+ log.LogInformation($"Processing GitHub owner {ghSetting.Owner}, repo {ghSetting.Repo}");
+ ProcessingResult ghResult = await doraSummaryController.UpdateDORASummaryItem(
+ ghSetting.Owner, "", ghSetting.Repo, ghSetting.Branch,
+ ghSetting.WorkflowName, ghSetting.WorkflowId,
+ ghSetting.ProductionResourceGroup,
+ numberOfDays, maxNumberOfItems,
+ null, true, true, true);
+ totalResults = ghResult.TotalResults;
+ }
+ }
+ DateTime endTime = DateTime.Now;
+ Console.WriteLine($"C# Timer trigger function complete at: {endTime} after updating {totalResults} records");
+ Console.WriteLine($"Processing finished in {(endTime - startTime).TotalSeconds}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/DevOpsMetrics.Cmd/ServiceApiClient.cs b/src/DevOpsMetrics.Cmd/ServiceApiClient.cs
new file mode 100644
index 00000000..f847f08f
--- /dev/null
+++ b/src/DevOpsMetrics.Cmd/ServiceApiClient.cs
@@ -0,0 +1,98 @@
+using System.Diagnostics;
+using System.Text;
+using DevOpsMetrics.Core.Models.AzureDevOps;
+using DevOpsMetrics.Core.Models.Common;
+using DevOpsMetrics.Core.Models.GitHub;
+using Microsoft.Extensions.Configuration;
+using Newtonsoft.Json;
+
+namespace DevOpsMetrics.Cmd
+{
+ public class ServiceApiClient
+ {
+ private readonly IConfiguration Configuration;
+ private readonly HttpClient Client;
+
+ public ServiceApiClient(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ Client = new HttpClient
+ {
+ BaseAddress = new Uri(Configuration["AppSettings:WebServiceURL"])
+ };
+ }
+
+ public async Task> GetAzureDevOpsSettings()
+ {
+ string url = $"/api/Settings/GetAzureDevOpsSettings";
+ return await GetResponse>(Client, url);
+ }
+
+ public async Task> GetGitHubSettings()
+ {
+ string url = $"/api/Settings/GetGitHubSettings";
+ return await GetResponse>(Client, url);
+ }
+
+ public async Task UpdateDORASummaryItem(
+ string owner, string project, string repository,
+ string branch, string workflowName, string workflowId,
+ string resourceGroup, int numberOfDays, int maxNumberOfItems,
+ bool isGitHub = true)
+ {
+ string url = $"/api/DORASummary/UpdateDORASummaryItem?owner={owner}&project={project}&repository={repository}&branch={branch}&workflowName={workflowName}&workflowId={workflowId}&resourceGroup={resourceGroup}&numberOfDays={numberOfDays}&maxNumberOfItems={maxNumberOfItems}&log=&useCache=true&isGitHub={isGitHub}";
+ return await GetResponse(Client, url);
+ }
+
+ public async Task UpdateDevOpsMonitoringEvent(MonitoringEvent monitoringEvent)
+ {
+ string url = $"/api/Settings/UpdateDevOpsMonitoringEvent";
+ return await PostResponse(Client, url, monitoringEvent);
+ }
+
+ private static async Task GetResponse(HttpClient client, string url)
+ {
+ T obj = default;
+ if (client != null && url != null)
+ {
+ Debug.WriteLine("Running url: " + client.BaseAddress.ToString() + url);
+ using (HttpResponseMessage response = await client.GetAsync(url))
+ {
+ //TODO: Add a flag in the service to turn this on and off.
+ if (response.IsSuccessStatusCode == false)
+ {
+ //Log the URL to help with debugging
+ }
+ response.EnsureSuccessStatusCode();
+ string responseBody = await response.Content.ReadAsStringAsync();
+ if (string.IsNullOrEmpty(responseBody) == false)
+ {
+ obj = JsonConvert.DeserializeObject(responseBody);
+ }
+ }
+ }
+ return obj;
+ }
+
+ private static async Task PostResponse(HttpClient client, string url, MonitoringEvent monitoringEvent)
+ {
+
+ if (client != null && url != null)
+ {
+ StringContent content = new(JsonConvert.SerializeObject(monitoringEvent), Encoding.UTF8, "application/json");
+
+ Debug.WriteLine("Running url: " + client.BaseAddress.ToString() + url);
+ using (HttpResponseMessage response = await client.PostAsync(url, content))
+ {
+ response.EnsureSuccessStatusCode();
+ //string responseBody = await response.Content.ReadAsStringAsync();
+ //if (string.IsNullOrEmpty(responseBody) == false)
+ //{
+ // obj = JsonConvert.DeserializeObject(responseBody);
+ //}
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/src/DevOpsMetrics.Cmd/appsettings.json b/src/DevOpsMetrics.Cmd/appsettings.json
new file mode 100644
index 00000000..b734d7e0
--- /dev/null
+++ b/src/DevOpsMetrics.Cmd/appsettings.json
@@ -0,0 +1,28 @@
+{
+ "AppSettings": {
+ "GitHubClientId": "", //Set in key vault
+ "GitHubClientSecret": "", //Set in key vault
+ "AzureDevOpsPatToken": "", //Set in key vault
+ "AzureStorageAccountConfigurationString": "",
+ "AzureStorageAccountContainerAzureDevOpsBuilds": "DevOpsAzureDevOpsBuilds",
+ "AzureStorageAccountContainerAzureDevOpsPRs": "DevOpsAzureDevOpsPRs",
+ "AzureStorageAccountContainerAzureDevOpsPRCommits": "DevOpsAzureDevOpsPRCommits",
+ "AzureStorageAccountContainerAzureDevOpsSettings": "DevOpsAzureDevOpsSettings",
+ "AzureStorageAccountContainerGitHubRuns": "DevOpsGitHubRuns",
+ "AzureStorageAccountContainerGitHubPRs": "DevOpsGitHubPRs",
+ "AzureStorageAccountContainerGitHubPRCommits": "DevOpsGitHubPRCommits",
+ "AzureStorageAccountContainerGitHubSettings": "DevOpsGitHubSettings",
+ "AzureStorageAccountContainerMTTR": "DevOpsMTTR",
+ "AzureStorageAccountContainerChangeFailureRate": "DevOpsChangeFailureRate",
+ "AzureStorageAccountDORASummaryItem": "DevOpsDORASummaryItem",
+ "AzureStorageAccountContainerTableLog": "DevOpsLog",
+ "WebServiceURL": "https://devops-prod-eu-service.azurewebsites.net/",
+ "KeyVaultURL": "https://devops-prod-eu-vault.vault.azure.net/",
+ "KeyVaultClientId": "",
+ "KeyVaultClientSecret": "",
+ "TenantId": "40244aae-8aee-4af5-b221-b480db24698b"
+ },
+ "ApplicationInsights": {
+ "InstrumentationKey": "dc3fb590-3612-4f09-8c95-e9378cba8654"
+ }
+}
diff --git a/src/DevOpsMetrics.Core/ChangeFailureRate.cs b/src/DevOpsMetrics.Core/ChangeFailureRate.cs
index 54171a19..0193c455 100644
--- a/src/DevOpsMetrics.Core/ChangeFailureRate.cs
+++ b/src/DevOpsMetrics.Core/ChangeFailureRate.cs
@@ -74,27 +74,27 @@ private List> GetChangeFailureRate(int numberOfDays
public static string GetChangeFailureRateRating(float changeFailureRate)
{
- string rating = "";
+ string rating;
if (changeFailureRate < 0)
{
rating = "None";
}
else if (changeFailureRate <= 0.15f) //0-15%
- {
- rating = "Elite";
- }
- else if (changeFailureRate <= 0.30f) //16-30% (not a typo, overriding table to <= 30% to create a range)
{
rating = "High";
}
- else if (changeFailureRate < 0.46f) //16-30% (not a typo, overriding table to < 46% to create a range)
+ else if (changeFailureRate <= 0.30f) //16-30%
{
rating = "Medium";
}
- else if (changeFailureRate >= 0.46f)// 16-30% (not a typo, overriding table to > 46% to create a range)
+ else if (changeFailureRate <= 1.00f) //46-60% (not a typo, overriding table from 46-60 to < 100% to create a range and capture all possible values)
{
rating = "Low";
}
+ else //no rating
+ {
+ rating = "None";
+ }
return rating;
}
}
diff --git a/src/DevOpsMetrics.Core/DataAccess/APIAccess/AzureDevOpsAPIAccess.cs b/src/DevOpsMetrics.Core/DataAccess/APIAccess/AzureDevOpsAPIAccess.cs
index d077c2f7..f8495586 100644
--- a/src/DevOpsMetrics.Core/DataAccess/APIAccess/AzureDevOpsAPIAccess.cs
+++ b/src/DevOpsMetrics.Core/DataAccess/APIAccess/AzureDevOpsAPIAccess.cs
@@ -71,7 +71,7 @@ private async static Task GetAzureDevOpsMessage(string url, string patTo
using (HttpResponseMessage response = await client.GetAsync(url))
{
//Throw a response exception
- response.EnsureSuccessStatusCode();
+ //response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
responseBody = await response.Content.ReadAsStringAsync();
diff --git a/src/DevOpsMetrics.Core/DataAccess/APIAccess/GitHubAPIAccess.cs b/src/DevOpsMetrics.Core/DataAccess/APIAccess/GitHubAPIAccess.cs
index 2f0afcfc..4d92c6a8 100644
--- a/src/DevOpsMetrics.Core/DataAccess/APIAccess/GitHubAPIAccess.cs
+++ b/src/DevOpsMetrics.Core/DataAccess/APIAccess/GitHubAPIAccess.cs
@@ -58,7 +58,7 @@ public static async Task GetGitHubPullRequestCommitsJArray(string client
public async static Task GetGitHubMessage(string url, string clientId, string clientSecret)
{
- Console.WriteLine($"Running GitHub url: {url}");
+ //Console.WriteLine($"Running GitHub url: {url}");
string responseBody = "";
if (url.Contains("api.github.com") == false)
{
@@ -77,7 +77,7 @@ public async static Task GetGitHubMessage(string url, string clientId, s
using (HttpResponseMessage response = await client.GetAsync(url))
{
//Throw a response exception
- response.EnsureSuccessStatusCode();
+ //response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
responseBody = await response.Content.ReadAsStringAsync();
diff --git a/src/DevOpsMetrics.Core/DataAccess/BuildsDA.cs b/src/DevOpsMetrics.Core/DataAccess/BuildsDA.cs
index bf77c9dd..2c32b7c0 100644
--- a/src/DevOpsMetrics.Core/DataAccess/BuildsDA.cs
+++ b/src/DevOpsMetrics.Core/DataAccess/BuildsDA.cs
@@ -18,11 +18,11 @@ public static async Task> GetAzureDevOpsBuilds(string pat
{
List builds = new();
JArray list = null;
- if (useCache == true)
+ if (useCache)
{
//Get the builds from Azure storage
AzureTableStorageDA daTableStorage = new();
- list = daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableAzureDevOpsBuilds, PartitionKeys.CreateBuildWorkflowPartitionKey(organization, project, buildName));
+ list = await daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableAzureDevOpsBuilds, PartitionKeys.CreateBuildWorkflowPartitionKey(organization, project, buildName));
}
else
{
@@ -51,11 +51,11 @@ public static async Task> GetGitHubActionRuns(string clie
{
List runs = new();
JArray list = null;
- if (useCache == true)
+ if (useCache)
{
//Get the builds from Azure storage
AzureTableStorageDA daTableStorage = new();
- list = daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableGitHubRuns, PartitionKeys.CreateBuildWorkflowPartitionKey(owner, repo, workflowName));
+ list = await daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableGitHubRuns, PartitionKeys.CreateBuildWorkflowPartitionKey(owner, repo, workflowName));
}
else
{
diff --git a/src/DevOpsMetrics.Core/DataAccess/ChangeFailureRateDA.cs b/src/DevOpsMetrics.Core/DataAccess/ChangeFailureRateDA.cs
index ecb293d5..c6864141 100644
--- a/src/DevOpsMetrics.Core/DataAccess/ChangeFailureRateDA.cs
+++ b/src/DevOpsMetrics.Core/DataAccess/ChangeFailureRateDA.cs
@@ -12,7 +12,7 @@ namespace DevOpsMetrics.Core.DataAccess
{
public class ChangeFailureRateDA
{
- public static ChangeFailureRateModel GetChangeFailureRate(bool getSampleData, TableStorageConfiguration tableStorageConfig,
+ public static async Task GetChangeFailureRate(bool getSampleData, TableStorageConfiguration tableStorageConfig,
DevOpsPlatform targetDevOpsPlatform, string organization_owner, string project_repo, string branch, string buildName_workflowName,
int numberOfDays, int maxNumberOfItems)
{
@@ -22,7 +22,7 @@ public static ChangeFailureRateModel GetChangeFailureRate(bool getSampleData, Ta
{
//Gets a list of change failure rate builds from Azure storage
AzureTableStorageDA daTableStorage = new();
- JArray list = daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableChangeFailureRate, PartitionKeys.CreateBuildWorkflowPartitionKey(organization_owner, project_repo, buildName_workflowName));
+ JArray list = await daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableChangeFailureRate, PartitionKeys.CreateBuildWorkflowPartitionKey(organization_owner, project_repo, buildName_workflowName));
List initialBuilds = JsonConvert.DeserializeObject>(list.ToString());
//Build the date list and then generate the change failure rate metric
@@ -106,7 +106,7 @@ public static async Task UpdateChangeFailureRate(TableStorageConfiguration
//Gets a list of change failure rate builds
AzureTableStorageDA daTableStorage = new();
string partitionKey = PartitionKeys.CreateBuildWorkflowPartitionKey(organization_owner, project_repo, buildName_workflowName);
- JArray list = daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableChangeFailureRate, partitionKey);
+ JArray list = await daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableChangeFailureRate, partitionKey);
List initialBuilds = JsonConvert.DeserializeObject>(list.ToString());
//Get the list of items we are going to process, within the date/day range
diff --git a/src/DevOpsMetrics.Core/DataAccess/DORASummaryDA.cs b/src/DevOpsMetrics.Core/DataAccess/DORASummaryDA.cs
index 38a827e1..a5e9a02e 100644
--- a/src/DevOpsMetrics.Core/DataAccess/DORASummaryDA.cs
+++ b/src/DevOpsMetrics.Core/DataAccess/DORASummaryDA.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Threading.Tasks;
using DevOpsMetrics.Core.DataAccess.TableStorage;
using DevOpsMetrics.Core.Models.Common;
using Newtonsoft.Json;
@@ -8,22 +9,32 @@ namespace DevOpsMetrics.Core.DataAccess
{
public class DORASummaryDA
{
- public static DORASummaryItem GetDORASummaryItem(TableStorageConfiguration tableStorageConfig,
- string owner, string repo)
+ public static async Task> GetDORASummaryItems(TableStorageConfiguration tableStorageConfig,
+ string owner)
{
- DORASummaryItem model = null;
AzureTableStorageDA da = new();
- JArray list = da.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableDORASummaryItem, owner);
+ JArray list = await da.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableDORASummaryItem, owner);
List doraItems = JsonConvert.DeserializeObject>(list.ToString());
- foreach (DORASummaryItem item in doraItems)
+ return doraItems;
+ }
+
+ public static async Task GetDORASummaryItem(TableStorageConfiguration tableStorageConfig,
+ string owner, string project, string repo)
+ {
+ DORASummaryItem result = null;
+ List doraItems = await GetDORASummaryItems(tableStorageConfig, owner);
+ if (doraItems != null)
{
- if (item.Repo.ToLower() == repo.ToLower())
+ foreach (DORASummaryItem item in doraItems)
{
- model = item;
- break;
+ if ((project != null && item.Project == project) || (item.Repo?.ToLower() == repo.ToLower()))
+ {
+ result = item;
+ break;
+ }
}
}
- return model;
+ return result;
}
}
diff --git a/src/DevOpsMetrics.Core/DataAccess/DeploymentFrequencyDA.cs b/src/DevOpsMetrics.Core/DataAccess/DeploymentFrequencyDA.cs
index 2020840a..68b7d562 100644
--- a/src/DevOpsMetrics.Core/DataAccess/DeploymentFrequencyDA.cs
+++ b/src/DevOpsMetrics.Core/DataAccess/DeploymentFrequencyDA.cs
@@ -107,7 +107,7 @@ public static async Task GetAzureDevOpsDeploymentFrequ
DeploymentName = buildName,
BuildList = builds,
DeploymentsPerDayMetric = 10f,
- DeploymentsPerDayMetricDescription = "Elite",
+ DeploymentsPerDayMetricDescription = "High",
NumberOfDays = numberOfDays,
MaxNumberOfItems = builds.Count,
TotalItems = builds.Count
@@ -212,7 +212,7 @@ public static async Task GetGitHubDeploymentFrequency(
DeploymentName = workflowName,
BuildList = builds,
DeploymentsPerDayMetric = 10f,
- DeploymentsPerDayMetricDescription = "Elite",
+ DeploymentsPerDayMetricDescription = "High",
NumberOfDays = numberOfDays,
MaxNumberOfItems = builds.Count,
TotalItems = builds.Count
diff --git a/src/DevOpsMetrics.Core/DataAccess/LeadTimeForChangesDA.cs b/src/DevOpsMetrics.Core/DataAccess/LeadTimeForChangesDA.cs
index 9faa0e8d..5134a370 100644
--- a/src/DevOpsMetrics.Core/DataAccess/LeadTimeForChangesDA.cs
+++ b/src/DevOpsMetrics.Core/DataAccess/LeadTimeForChangesDA.cs
@@ -166,7 +166,7 @@ public static async Task GetAzureDevOpsLeadTimesForChan
AverageBuildHours = 1f,
AveragePullRequestHours = 12f,
LeadTimeForChangesMetric = 12f + 1f,
- LeadTimeForChangesMetricDescription = "Elite",
+ LeadTimeForChangesMetricDescription = "High",
PullRequests = samplePullRequests,
NumberOfDays = numberOfDays,
MaxNumberOfItems = samplePullRequests.Count,
@@ -282,10 +282,6 @@ public static async Task GetGitHubLeadTimesForChanges(b
{
pullRequest.Status = "inProgress";
}
- else
- {
- pullRequest.Status = pullRequest.Status;
- }
leadTimeForChangesList.Add(new KeyValuePair(minTime, pullRequest.Duration));
pullRequests.Add(pullRequest);
@@ -327,7 +323,7 @@ public static async Task GetGitHubLeadTimesForChanges(b
AverageBuildHours = averageBuildHours,
AveragePullRequestHours = leadTime,
LeadTimeForChangesMetric = leadTime + averageBuildHours,
- LeadTimeForChangesMetricDescription = LeadTimeForChanges.GetLeadTimeForChangesRating(leadTime),
+ LeadTimeForChangesMetricDescription = LeadTimeForChanges.GetLeadTimeForChangesRating(leadTime + averageBuildHours),
PullRequests = utility.GetLastNItems(pullRequests, maxNumberOfItems),
NumberOfDays = numberOfDays,
MaxNumberOfItems = uiPullRequests.Count,
@@ -346,7 +342,7 @@ public static async Task GetGitHubLeadTimesForChanges(b
AverageBuildHours = 1f,
AveragePullRequestHours = 20.33f,
LeadTimeForChangesMetric = 20.33f + 1f,
- LeadTimeForChangesMetricDescription = "Elite",
+ LeadTimeForChangesMetricDescription = "High",
PullRequests = samplePullRequests,
NumberOfDays = numberOfDays,
MaxNumberOfItems = samplePullRequests.Count,
diff --git a/src/DevOpsMetrics.Core/DataAccess/MeanTimeToRestoreDA.cs b/src/DevOpsMetrics.Core/DataAccess/MeanTimeToRestoreDA.cs
index 957ccc41..40b18377 100644
--- a/src/DevOpsMetrics.Core/DataAccess/MeanTimeToRestoreDA.cs
+++ b/src/DevOpsMetrics.Core/DataAccess/MeanTimeToRestoreDA.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using DevOpsMetrics.Core.DataAccess.Common;
using DevOpsMetrics.Core.DataAccess.TableStorage;
using DevOpsMetrics.Core.Models.Azure;
@@ -11,7 +12,7 @@ namespace DevOpsMetrics.Core.DataAccess
{
public class MeanTimeToRestoreDA
{
- public static MeanTimeToRestoreModel GetAzureMeanTimeToRestore(bool getSampleData,
+ public static async Task GetAzureMeanTimeToRestore(bool getSampleData,
TableStorageConfiguration tableStorageConfig,
DevOpsPlatform targetDevOpsPlatform, string resourceGroup,
int numberOfDays, int maxNumberOfItems)
@@ -27,7 +28,7 @@ public static MeanTimeToRestoreModel GetAzureMeanTimeToRestore(bool getSampleDat
//Pull the events from the table storage
AzureTableStorageDA daTableStorage = new();
- JArray list = daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableMTTR, resourceGroup);
+ JArray list = await daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableMTTR, resourceGroup);
List alerts = new();
foreach (JToken item in list)
{
diff --git a/src/DevOpsMetrics.Core/DataAccess/PullRequestsDA.cs b/src/DevOpsMetrics.Core/DataAccess/PullRequestsDA.cs
index 06af6def..e0f8cefe 100644
--- a/src/DevOpsMetrics.Core/DataAccess/PullRequestsDA.cs
+++ b/src/DevOpsMetrics.Core/DataAccess/PullRequestsDA.cs
@@ -17,11 +17,11 @@ public static async Task GetAzureDevOpsPullRequest(string patToke
{
List prs = new();
JArray list;
- if (useCache == true)
+ if (useCache)
{
//Get the pull requests from Azure storage
AzureTableStorageDA daTableStorage = new();
- list = daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableAzureDevOpsPRs, PartitionKeys.CreateGitHubPRPartitionKey(organization, project));
+ list = await daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableAzureDevOpsPRs, PartitionKeys.CreateGitHubPRPartitionKey(organization, project));
}
else
{
@@ -49,11 +49,11 @@ public static async Task GetAzureDevOpsPullRequest(string patToke
public static async Task> GetAzureDevOpsPullRequestCommits(string patToken, TableStorageConfiguration tableStorageConfig, string organization, string project, string repository, string pullRequestId, bool useCache)
{
JArray list;
- if (useCache == true)
+ if (useCache)
{
//Get the commits from Azure storage
AzureTableStorageDA daTableStorage = new();
- list = daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableAzureDevOpsPRCommits, PartitionKeys.CreateAzureDevOpsPRCommitPartitionKey(organization, project, pullRequestId));
+ list = await daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableAzureDevOpsPRCommits, PartitionKeys.CreateAzureDevOpsPRCommitPartitionKey(organization, project, pullRequestId));
}
else
{
@@ -69,11 +69,11 @@ public static async Task GetGitHubPullRequest(string clientId, string
{
List prs = new();
JArray list;
- if (useCache == true)
+ if (useCache)
{
//Get the pull requests from Azure storage
AzureTableStorageDA daTableStorage = new();
- list = daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableGitHubPRs, PartitionKeys.CreateGitHubPRPartitionKey(owner, repo));
+ list = await daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableGitHubPRs, PartitionKeys.CreateGitHubPRPartitionKey(owner, repo));
}
else
{
@@ -101,11 +101,11 @@ public static async Task GetGitHubPullRequest(string clientId, string
public static async Task> GetGitHubPullRequestCommits(string clientId, string clientSecret, TableStorageConfiguration tableStorageConfig, string owner, string repo, string pull_number, bool useCache)
{
JArray list;
- if (useCache == true)
+ if (useCache)
{
//Get the commits from Azure storage
AzureTableStorageDA daTableStorage = new();
- list = daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableGitHubPRCommits, PartitionKeys.CreateGitHubPRCommitPartitionKey(owner, repo, pull_number));
+ list = await daTableStorage.GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableGitHubPRCommits, PartitionKeys.CreateGitHubPRCommitPartitionKey(owner, repo, pull_number));
}
else
{
diff --git a/src/DevOpsMetrics.Core/DataAccess/TableStorage/AzureTableStorageDA.cs b/src/DevOpsMetrics.Core/DataAccess/TableStorage/AzureTableStorageDA.cs
index a8ec7196..7013e9d4 100644
--- a/src/DevOpsMetrics.Core/DataAccess/TableStorage/AzureTableStorageDA.cs
+++ b/src/DevOpsMetrics.Core/DataAccess/TableStorage/AzureTableStorageDA.cs
@@ -23,14 +23,18 @@ public class AzureTableStorageDA : IAzureTableStorageDA
/// Partition key to filter by
/// Include Partition and row key metadata (for debugging)
///
- public JArray GetTableStorageItemsFromStorage(TableStorageConfiguration tableStorageConfig, string tableName, string partitionKey, bool includePartitionAndRowKeys = false)
+ public async Task GetTableStorageItemsFromStorage(TableStorageConfiguration tableStorageConfig, string tableName, string partitionKey, bool includePartitionAndRowKeys = false)
{
TableStorageCommonDA tableDA = new(tableStorageConfig.StorageAccountConnectionString, tableName);
- List items = tableDA.GetItems(partitionKey);
+ List items = await tableDA.GetItems(partitionKey);
+ if (items == null)
+ {
+ items = new();
+ }
JArray list = new();
foreach (AzureStorageTableModel item in items)
{
- if (includePartitionAndRowKeys == true)
+ if (includePartitionAndRowKeys)
{
string data = item.Data?.ToString();
list.Add(
@@ -48,11 +52,11 @@ public JArray GetTableStorageItemsFromStorage(TableStorageConfiguration tableSto
return list;
}
- public List GetAzureDevOpsSettingsFromStorage(TableStorageConfiguration tableStorageConfig, string settingsTable, string rowKey)
+ public async Task> GetAzureDevOpsSettingsFromStorage(TableStorageConfiguration tableStorageConfig, string settingsTable, string rowKey)
{
List settings = null;
string partitionKey = "AzureDevOpsSettings";
- JArray list = GetTableStorageItemsFromStorage(tableStorageConfig, settingsTable, partitionKey);
+ JArray list = await GetTableStorageItemsFromStorage(tableStorageConfig, settingsTable, partitionKey);
if (list != null)
{
settings = JsonConvert.DeserializeObject>(list.ToString());
@@ -70,11 +74,11 @@ public List GetAzureDevOpsSettingsFromStorage(TableStorageC
}
}
- public List GetGitHubSettingsFromStorage(TableStorageConfiguration tableStorageConfig, string settingsTable, string rowKey)
+ public async Task> GetGitHubSettingsFromStorage(TableStorageConfiguration tableStorageConfig, string settingsTable, string rowKey)
{
List settings = null;
string partitionKey = "GitHubSettings";
- JArray list = GetTableStorageItemsFromStorage(tableStorageConfig, settingsTable, partitionKey);
+ JArray list = await GetTableStorageItemsFromStorage(tableStorageConfig, settingsTable, partitionKey);
if (list != null)
{
settings = JsonConvert.DeserializeObject>(list.ToString());
@@ -92,10 +96,10 @@ public List GetGitHubSettingsFromStorage(TableStorageConfigurati
}
}
- public List GetProjectLogsFromStorage(TableStorageConfiguration tableStorageConfig, string partitionKey)
+ public async Task> GetProjectLogsFromStorage(TableStorageConfiguration tableStorageConfig, string partitionKey)
{
List logs = null;
- JArray list = GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableLog, partitionKey, true);
+ JArray list = await GetTableStorageItemsFromStorage(tableStorageConfig, tableStorageConfig.TableLog, partitionKey, true);
if (list != null)
{
logs = JsonConvert.DeserializeObject>(list.ToString());
@@ -109,7 +113,10 @@ public async Task UpdateAzureDevOpsBuildsInStorage(string patToken, TableSt
int numberOfDays, int maxNumberOfItems)
{
JArray items = await AzureDevOpsAPIAccess.GetAzureDevOpsBuildsJArray(patToken, organization, project);
-
+ if (items == null)
+ {
+ items = new();
+ }
int itemsAdded = 0;
TableStorageCommonDA tableBuildsDA = new(tableStorageConfig.StorageAccountConnectionString, tableStorageConfig.TableAzureDevOpsBuilds);
TableStorageCommonDA tableChangeFailureRateDA = new(tableStorageConfig.StorageAccountConnectionString, tableStorageConfig.TableChangeFailureRate);
@@ -124,7 +131,7 @@ public async Task UpdateAzureDevOpsBuildsInStorage(string patToken, TableSt
string partitionKey = PartitionKeys.CreateBuildWorkflowPartitionKey(organization, project, buildName);
string rowKey = build.buildNumber;
AzureStorageTableModel newItem = new(partitionKey, rowKey, item.ToString());
- if (await tableBuildsDA.AddItem(newItem) == true)
+ if (await tableBuildsDA.AddItem(newItem))
{
itemsAdded++;
}
@@ -154,7 +161,10 @@ public async Task UpdateAzureDevOpsPullRequestsInStorage(string patToken, T
int numberOfDays, int maxNumberOfItems)
{
JArray items = await AzureDevOpsAPIAccess.GetAzureDevOpsPullRequestsJArray(patToken, organization, project, repository);
-
+ if (items == null)
+ {
+ items = new();
+ }
int itemsAdded = 0;
TableStorageCommonDA tableDA = new(tableStorageConfig.StorageAccountConnectionString, tableStorageConfig.TableAzureDevOpsPRs);
//Check each build to see if it's in storage, adding the items not in storage
@@ -165,7 +175,7 @@ public async Task UpdateAzureDevOpsPullRequestsInStorage(string patToken, T
string partitionKey = PartitionKeys.CreateAzureDevOpsPRPartitionKey(organization, project);
string rowKey = pullRequest.PullRequestId;
AzureStorageTableModel newItem = new(partitionKey, rowKey, item.ToString());
- if (await tableDA.AddItem(newItem) == true)
+ if (await tableDA.AddItem(newItem))
{
itemsAdded++;
}
@@ -183,7 +193,10 @@ public async Task UpdateAzureDevOpsPullRequestCommitsInStorage(string patTo
int numberOfDays, int maxNumberOfItems)
{
JArray items = await AzureDevOpsAPIAccess.GetAzureDevOpsPullRequestCommitsJArray(patToken, organization, project, repository, pullRequestId);
-
+ if (items == null)
+ {
+ items = new();
+ }
int itemsAdded = 0;
TableStorageCommonDA tableDA = new(tableStorageConfig.StorageAccountConnectionString, tableStorageConfig.TableAzureDevOpsPRCommits);
//Check each build to see if it's in storage, adding the items not in storage
@@ -194,7 +207,7 @@ public async Task UpdateAzureDevOpsPullRequestCommitsInStorage(string patTo
string partitionKey = PartitionKeys.CreateAzureDevOpsPRCommitPartitionKey(organization, project, pullRequestId);
string rowKey = pullRequestCommit.commitId;
AzureStorageTableModel newItem = new(partitionKey, rowKey, item.ToString());
- if (await tableDA.AddItem(newItem) == true)
+ if (await tableDA.AddItem(newItem))
{
itemsAdded++;
}
@@ -208,6 +221,10 @@ public async Task UpdateGitHubActionRunsInStorage(string clientId, string c
int numberOfDays, int maxNumberOfItems)
{
JArray items = await GitHubAPIAccess.GetGitHubActionRunsJArray(clientId, clientSecret, owner, repo, workflowId);
+ if (items == null)
+ {
+ items = new();
+ }
Debug.WriteLine($"{items.Count} builds found for {owner}/{repo}/{workflowName}");
int itemsAdded = 0;
@@ -224,7 +241,7 @@ public async Task UpdateGitHubActionRunsInStorage(string clientId, string c
string partitionKey = PartitionKeys.CreateBuildWorkflowPartitionKey(owner, repo, workflowName);
string rowKey = build.run_number;
AzureStorageTableModel newItem = new(partitionKey, rowKey, item.ToString());
- if (await tableBuildDA.AddItem(newItem) == true)
+ if (await tableBuildDA.AddItem(newItem))
{
itemsAdded++;
}
@@ -257,7 +274,7 @@ public async Task UpdateChangeFailureRate(TableStorageCommonDA tableChangeF
string rowKey = newBuild.Id;
string json = JsonConvert.SerializeObject(newBuild);
AzureStorageTableModel newItem = new(partitionKey, rowKey, json);
- if (await tableChangeFailureRateDA.AddItem(newItem, forceUpdate) == true)
+ if (await tableChangeFailureRateDA.AddItem(newItem, forceUpdate))
{
itemsAdded++;
}
@@ -269,7 +286,10 @@ public async Task UpdateGitHubActionPullRequestsInStorage(string clientId,
int numberOfDays, int maxNumberOfItems)
{
JArray items = await GitHubAPIAccess.GetGitHubPullRequestsJArray(clientId, clientSecret, owner, repo, branch);
-
+ if (items == null)
+ {
+ items = new();
+ }
int itemsAdded = 0;
TableStorageCommonDA tableDA = new(tableStorageConfig.StorageAccountConnectionString, tableStorageConfig.TableGitHubPRs);
//Check each build to see if it's in storage, adding the items not in storage
@@ -277,12 +297,12 @@ public async Task UpdateGitHubActionPullRequestsInStorage(string clientId,
{
GitHubPR pr = JsonConvert.DeserializeObject(item.ToString());
- if (pr.state == "closed" & pr.merged_at != null)
+ if (pr.state == "closed" && pr.merged_at != null)
{
string partitionKey = PartitionKeys.CreateGitHubPRPartitionKey(owner, repo);
string rowKey = pr.number;
string json = item.ToString();
- Debug.WriteLine($"PartitionKey: {partitionKey}, RowKey: {rowKey}, Length: {json.Length}");
+ //Debug.WriteLine($"PartitionKey: {partitionKey}, RowKey: {rowKey}, Length: {json.Length}");
if (item.ToString().Length > (1024 * 32)) //1024 x 32 is the column limit
{
@@ -292,7 +312,7 @@ public async Task UpdateGitHubActionPullRequestsInStorage(string clientId,
json = o.ToString();
}
AzureStorageTableModel newItem = new(partitionKey, rowKey, json);
- if (await tableDA.AddItem(newItem) == true)
+ if (await tableDA.AddItem(newItem))
{
itemsAdded++;
}
@@ -307,7 +327,10 @@ public async Task UpdateGitHubActionPullRequestCommitsInStorage(string clie
string owner, string repo, string pull_number)
{
JArray items = await GitHubAPIAccess.GetGitHubPullRequestCommitsJArray(clientId, clientSecret, owner, repo, pull_number);
-
+ if (items == null)
+ {
+ items = new();
+ }
int itemsAdded = 0;
TableStorageCommonDA tableDA = new(tableStorageConfig.StorageAccountConnectionString, tableStorageConfig.TableGitHubPRCommits);
//Check each build to see if it's in storage, adding the items not in storage
@@ -318,7 +341,7 @@ public async Task UpdateGitHubActionPullRequestCommitsInStorage(string clie
string partitionKey = PartitionKeys.CreateGitHubPRCommitPartitionKey(owner, repo, pull_number);
string rowKey = commit.sha;
AzureStorageTableModel newItem = new(partitionKey, rowKey, item.ToString());
- if (await tableDA.AddItem(newItem) == true)
+ if (await tableDA.AddItem(newItem))
{
itemsAdded++;
}
@@ -397,10 +420,18 @@ public async Task UpdateProjectLogInStorage(TableStorageConfiguration tabl
}
public static async Task UpdateDORASummaryItem(TableStorageConfiguration tableStorageConfig,
- string owner, string repo, DORASummaryItem DORASummaryItem)
+ string owner, string project, string repo, DORASummaryItem DORASummaryItem)
{
string partitionKey = owner;
- string rowKey = repo;
+ string rowKey;
+ if (string.IsNullOrEmpty(project))
+ {
+ rowKey = repo;
+ }
+ else
+ {
+ rowKey = project;
+ }
string json = JsonConvert.SerializeObject(DORASummaryItem);
AzureStorageTableModel newItem = new(partitionKey, rowKey, json);
TableStorageCommonDA tableDA = new(tableStorageConfig.StorageAccountConnectionString, tableStorageConfig.TableDORASummaryItem);
diff --git a/src/DevOpsMetrics.Core/DataAccess/TableStorage/IAzureTableStorageDA.cs b/src/DevOpsMetrics.Core/DataAccess/TableStorage/IAzureTableStorageDA.cs
index c287ac58..3c150d57 100644
--- a/src/DevOpsMetrics.Core/DataAccess/TableStorage/IAzureTableStorageDA.cs
+++ b/src/DevOpsMetrics.Core/DataAccess/TableStorage/IAzureTableStorageDA.cs
@@ -9,9 +9,9 @@ namespace DevOpsMetrics.Core.DataAccess.TableStorage
{
public interface IAzureTableStorageDA
{
- JArray GetTableStorageItemsFromStorage(TableStorageConfiguration tableStorageConfig, string tableName, string partitionKey, bool includePartitionAndRowKeys = false);
- List GetAzureDevOpsSettingsFromStorage(TableStorageConfiguration tableStorageConfig, string settingsTable, string rowKey);
- List GetGitHubSettingsFromStorage(TableStorageConfiguration tableStorageConfig, string settingsTable, string rowKey);
+ Task GetTableStorageItemsFromStorage(TableStorageConfiguration tableStorageConfig, string tableName, string partitionKey, bool includePartitionAndRowKeys = false);
+ Task> GetAzureDevOpsSettingsFromStorage(TableStorageConfiguration tableStorageConfig, string settingsTable, string rowKey);
+ Task> GetGitHubSettingsFromStorage(TableStorageConfiguration tableStorageConfig, string settingsTable, string rowKey);
Task UpdateAzureDevOpsBuildsInStorage(string patToken, TableStorageConfiguration tableStorageConfig, string organization, string project, string branch, string buildName, string buildId, int numberOfDays, int maxNumberOfItems);
Task UpdateAzureDevOpsPullRequestCommitsInStorage(string patToken, TableStorageConfiguration tableStorageConfig, string organization, string project, string repository, string pullRequestId, int numberOfDays, int maxNumberOfItems);
Task UpdateAzureDevOpsPullRequestsInStorage(string patToken, TableStorageConfiguration tableStorageConfig, string organization, string project, string repository, int numberOfDays, int maxNumberOfItems);
@@ -22,7 +22,7 @@ public interface IAzureTableStorageDA
Task UpdateGitHubActionPullRequestsInStorage(string clientId, string clientSecret, TableStorageConfiguration tableStorageConfig, string owner, string repo, string branch, int numberOfDays, int maxNumberOfItems);
Task UpdateGitHubActionRunsInStorage(string clientId, string clientSecret, TableStorageConfiguration tableStorageConfig, string owner, string repo, string branch, string workflowName, string workflowId, int numberOfDays, int maxNumberOfItems);
Task UpdateGitHubSettingInStorage(TableStorageConfiguration tableStorageConfig, string settingsTable, string owner, string repo, string branch, string workflowName, string workflowId, string resourceGroupName, int itemOrder, bool showSetting);
- List GetProjectLogsFromStorage(TableStorageConfiguration tableStorageConfig, string partitionKey);
+ Task> GetProjectLogsFromStorage(TableStorageConfiguration tableStorageConfig, string partitionKey);
Task UpdateProjectLogInStorage(TableStorageConfiguration tableStorageConfig, ProjectLog log);
}
}
\ No newline at end of file
diff --git a/src/DevOpsMetrics.Core/DataAccess/TableStorage/PartitionKeys.cs b/src/DevOpsMetrics.Core/DataAccess/TableStorage/PartitionKeys.cs
index c827ef98..af0559df 100644
--- a/src/DevOpsMetrics.Core/DataAccess/TableStorage/PartitionKeys.cs
+++ b/src/DevOpsMetrics.Core/DataAccess/TableStorage/PartitionKeys.cs
@@ -1,6 +1,8 @@
-namespace DevOpsMetrics.Core.DataAccess.TableStorage
+using System.Text.RegularExpressions;
+
+namespace DevOpsMetrics.Core.DataAccess.TableStorage
{
- public static class PartitionKeys
+ public static partial class PartitionKeys
{
public static string CreateAzureDevOpsSettingsPartitionKey(string organization, string project, string repository)
{
@@ -11,7 +13,7 @@ public static string CreateAzureDevOpsSettingsPartitionKeyPatToken(string organi
{
//add a azdo/Azure DevOps prefix, remove _/underscores and append a suffix for the pat token
string result = "azdo" + CreateAzureDevOpsSettingsPartitionKey(organization, project, repository) + "patToken";
- return result.Replace("_", "");
+ return CleanKey(result.Replace("_", ""));
}
public static string CreateBuildWorkflowPartitionKey(string organization_owner, string project_repo, string buildName_workflowName)
@@ -38,14 +40,14 @@ public static string CreateGitHubSettingsPartitionKeyClientId(string owner, stri
{
//add a gh/github prefix, remove _/underscores and append a suffix for the client id
string result = "gh" + CreateGitHubSettingsPartitionKey(owner, repo) + "clientId";
- return result.Replace("_", "");
+ return CleanKey(result.Replace("_", ""));
}
public static string CreateGitHubSettingsPartitionKeyClientSecret(string owner, string repo)
{
//add a gh/github prefix, remove _/underscores and append a suffix for the client secret
string result = "gh" + CreateGitHubSettingsPartitionKey(owner, repo) + "clientSecret";
- return result.Replace("_", "");
+ return CleanKey(result.Replace("_", ""));
}
public static string CreateGitHubPRPartitionKey(string owner, string repo)
@@ -57,5 +59,11 @@ public static string CreateGitHubPRCommitPartitionKey(string owner, string repo,
{
return owner + "_" + repo + "_" + pullRequestId;
}
+
+ //Only Alphanumerics and hyphens allowed https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/resource-name-rules#microsoftkeyvault
+ public static string CleanKey(string name)
+ {
+ return Regex.Replace(name, @"[^a-zA-Z0-9]+", "-").Trim('-');
+ }
}
}
diff --git a/src/DevOpsMetrics.Core/DataAccess/TableStorage/TableStorageCommonDA.cs b/src/DevOpsMetrics.Core/DataAccess/TableStorage/TableStorageCommonDA.cs
index 9e411fd7..8cb2051a 100644
--- a/src/DevOpsMetrics.Core/DataAccess/TableStorage/TableStorageCommonDA.cs
+++ b/src/DevOpsMetrics.Core/DataAccess/TableStorage/TableStorageCommonDA.cs
@@ -1,8 +1,9 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Azure;
+using Azure.Data.Tables;
using DevOpsMetrics.Core.Models.Azure;
-using Microsoft.Azure.Cosmos.Table;
namespace DevOpsMetrics.Core.DataAccess.TableStorage
{
@@ -21,32 +22,25 @@ public TableStorageCommonDA()
{
}
- private CloudTable CreateConnection()
+ private TableClient CreateConnection()
{
- //CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(AccountName, AccessKey), true);
- CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationString);
-
- // Create the table client.
- CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
-
- // Get a reference to a table named "items"
- CloudTable table = tableClient.GetTableReference(TableName);
+ // Create a TableServiceClient for service level operations
+ TableServiceClient serviceClient = new(ConfigurationString);
// Create the table if it doesn't exist
- //table.CreateIfNotExists(); // DON"T use this, it throws an internal 409 in App insights: https://stackoverflow.com/questions/48893519/azure-table-storage-exception-409-conflict-unexpected
- if (!table.Exists())
- {
- table.Create();
- }
+ TableClient tableClient = serviceClient.GetTableClient(TableName);
- return table;
+ // Create the table if it doesn't exist.
+ tableClient.CreateIfNotExists();
+
+ return tableClient;
}
public async Task AddItem(AzureStorageTableModel data, bool forceUpdate = false)
{
//Check if the item exists in storage
AzureStorageTableModel item = await GetItem(data.PartitionKey, data.RowKey);
- if (item == null || forceUpdate == true)
+ if (item == null || forceUpdate)
{
await SaveItem(data);
return true; //data saved to table!
@@ -62,39 +56,37 @@ public async Task GetItem(string partitionKey, string ro
//prepare the partition key
partitionKey = EncodePartitionKey(partitionKey);
- CloudTable table = CreateConnection();
-
- // Create a retrieve operation that takes a customer entity.
- TableOperation retrieveOperation = TableOperation.Retrieve(partitionKey, rowKey);
+ TableClient tableClient = CreateConnection();
- // Execute the retrieve operation.
- TableResult retrievedResult = await table.ExecuteAsync(retrieveOperation);
-
- return (AzureStorageTableModel)retrievedResult.Result;
+ // Get the entity.
+ NullableResponse temp = await tableClient.GetEntityIfExistsAsync(partitionKey, rowKey);
+ if (temp.HasValue)
+ {
+ return await tableClient.GetEntityAsync(partitionKey, rowKey);
+ }
+ else
+ {
+ return null;
+ }
}
//This can't be async, because of how it queries the underlying data
- public List GetItems(string partitionKey)
+ public async Task> GetItems(string partitionKey)
{
partitionKey = EncodePartitionKey(partitionKey);
- CloudTable table = CreateConnection();
-
- // execute the query on the table
- List list = table.CreateQuery()
- .Where(ent => ent.PartitionKey == partitionKey)
- .ToList();
+ TableClient tableClient = CreateConnection();
+ AsyncPageable results = tableClient.QueryAsync(e => e.PartitionKey == partitionKey);
+ List list = await results.ToListAsync();
return list;
}
public async Task SaveItem(AzureStorageTableModel data)
{
- CloudTable table = CreateConnection();
+ TableClient tableClient = CreateConnection();
+ await tableClient.UpsertEntityAsync(data);
- // Create the TableOperation that inserts/merges the entity.
- TableOperation operation = TableOperation.InsertOrMerge(data);
- await table.ExecuteAsync(operation);
return true;
}
@@ -124,23 +116,26 @@ public async Task SaveItem(AzureStorageTableModel data)
public static string EncodePartitionKey(string text)
{
- //The forward slash(/) character
- //The backslash(\) character
- //The number sign(#) character
- //The question mark (?) character
- text = text.Replace("/", "_");
- //text = text.Replace("\\", "_");
- //text = text.Replace("#", "_");
- //text = text.Replace("?", "_");
-
- ////Control characters from U+0000 to U+001F, including:
- ////The horizontal tab(\t) character
- //text = text.Replace("\t", "_");
- ////The linefeed(\n) character
- //text = text.Replace("\n", "_");
- ////The carriage return (\r) character
- //text = text.Replace("\r", "_");
- ////Control characters from U + 007F to U+009F
+ if (string.IsNullOrEmpty(text) == false)
+ {
+ //The forward slash(/) character
+ //The backslash(\) character
+ //The number sign(#) character
+ //The question mark (?) character
+ text = text.Replace("/", "_");
+ //text = text.Replace("\\", "_");
+ //text = text.Replace("#", "_");
+ //text = text.Replace("?", "_");
+
+ ////Control characters from U+0000 to U+001F, including:
+ ////The horizontal tab(\t) character
+ //text = text.Replace("\t", "_");
+ ////The linefeed(\n) character
+ //text = text.Replace("\n", "_");
+ ////The carriage return (\r) character
+ //text = text.Replace("\r", "_");
+ ////Control characters from U + 007F to U+009F
+ }
return text;
}
diff --git a/src/DevOpsMetrics.Core/DeploymentFrequency.cs b/src/DevOpsMetrics.Core/DeploymentFrequency.cs
index 948b9fe7..4edac705 100644
--- a/src/DevOpsMetrics.Core/DeploymentFrequency.cs
+++ b/src/DevOpsMetrics.Core/DeploymentFrequency.cs
@@ -70,26 +70,22 @@ public static string GetDeploymentFrequencyRating(float deploymentsPerDay)
float dailyDeployment = 1f;
//float weeklyDeployment = 1f / 7f;
float monthlyDeployment = 1f / 30f;
- float everySixMonthsDeployment = 1f / (6f * 30f); //Every 6 months
+ //float everySixMonthsDeployment = 1f / (6f * 30f); //Every 6 months
string rating = "";
if (deploymentsPerDay <= 0f)
{
rating = "None";
}
- else if (deploymentsPerDay >= dailyDeployment) //NOTE: Does not capture on-demand deployments
- {
- rating = "Elite";
- }
- else if (deploymentsPerDay < dailyDeployment && deploymentsPerDay >= monthlyDeployment)
+ else if (deploymentsPerDay >= dailyDeployment) //NOTE: Assumes on-demand deployments are <1 day
{
rating = "High";
}
- else if (deploymentsPerDay < monthlyDeployment && deploymentsPerDay >= everySixMonthsDeployment)
+ else if (deploymentsPerDay < dailyDeployment && deploymentsPerDay >= monthlyDeployment) //Captures days to months
{
rating = "Medium";
}
- else if (deploymentsPerDay < everySixMonthsDeployment)
+ else if (deploymentsPerDay < monthlyDeployment)
{
rating = "Low";
}
diff --git a/src/DevOpsMetrics.Core/DevOpsMetrics.Core.csproj b/src/DevOpsMetrics.Core/DevOpsMetrics.Core.csproj
index a4b9414c..cb4079c6 100644
--- a/src/DevOpsMetrics.Core/DevOpsMetrics.Core.csproj
+++ b/src/DevOpsMetrics.Core/DevOpsMetrics.Core.csproj
@@ -1,13 +1,14 @@
-
+
- net7.0
+ net10.0
-
+
-
+
+
diff --git a/src/DevOpsMetrics.Core/GlobalSuppressions.cs b/src/DevOpsMetrics.Core/GlobalSuppressions.cs
index 20197f8e..cabd9003 100644
--- a/src/DevOpsMetrics.Core/GlobalSuppressions.cs
+++ b/src/DevOpsMetrics.Core/GlobalSuppressions.cs
@@ -11,3 +11,9 @@
//[assembly: SuppressMessage("Style", "IDE0066:Convert switch statement to expression", Justification = "This is a 'silly' rule. The switch is clearer.", Scope = "member", Target = "~M:DevOpsMetrics.Core.Models.Common.Badges.BadgeColor(System.String)~System.String")]
[assembly: SuppressMessage("Style", "IDE0066:Convert switch statement to expression", Justification = "This is a 'silly' rule. The switch is clearer.", Scope = "member", Target = "~M:DevOpsMetrics.Core.Models.Common.Badges.BadgeURL(System.String,System.String)~System.String")]
[assembly: SuppressMessage("CodeQuality", "IDE0077:Avoid legacy format target in 'SuppressMessageAttribute'", Justification = "")]
+[assembly: SuppressMessage("Style", "IDE0270:Use coalesce expression", Justification = "Does not improve readability", Scope = "member", Target = "~M:DevOpsMetrics.Core.DataAccess.TableStorage.AzureTableStorageDA.UpdateGitHubActionPullRequestCommitsInStorage(System.String,System.String,DevOpsMetrics.Core.Models.Common.TableStorageConfiguration,System.String,System.String,System.String)~System.Threading.Tasks.Task{System.Int32}")]
+[assembly: SuppressMessage("Style", "IDE0074:Use compound assignment", Justification = "Does not improve readability", Scope = "member", Target = "~M:DevOpsMetrics.Core.DataAccess.TableStorage.AzureTableStorageDA.UpdateGitHubActionPullRequestCommitsInStorage(System.String,System.String,DevOpsMetrics.Core.Models.Common.TableStorageConfiguration,System.String,System.String,System.String)~System.Threading.Tasks.Task{System.Int32}")]
+[assembly: SuppressMessage("Style", "IDE0270:Use coalesce expression", Justification = "Does not improve readability", Scope = "member", Target = "~M:DevOpsMetrics.Core.DataAccess.TableStorage.AzureTableStorageDA.UpdateGitHubActionPullRequestsInStorage(System.String,System.String,DevOpsMetrics.Core.Models.Common.TableStorageConfiguration,System.String,System.String,System.String,System.Int32,System.Int32)~System.Threading.Tasks.Task{System.Int32}")]
+[assembly: SuppressMessage("Style", "IDE0074:Use compound assignment", Justification = "Does not improve readability", Scope = "member", Target = "~M:DevOpsMetrics.Core.DataAccess.TableStorage.AzureTableStorageDA.UpdateGitHubActionPullRequestsInStorage(System.String,System.String,DevOpsMetrics.Core.Models.Common.TableStorageConfiguration,System.String,System.String,System.String,System.Int32,System.Int32)~System.Threading.Tasks.Task{System.Int32}")]
+[assembly: SuppressMessage("Style", "IDE0270:Use coalesce expression", Justification = "Does not improve readability", Scope = "member", Target = "~M:DevOpsMetrics.Core.DataAccess.TableStorage.AzureTableStorageDA.UpdateGitHubActionRunsInStorage(System.String,System.String,DevOpsMetrics.Core.Models.Common.TableStorageConfiguration,System.String,System.String,System.String,System.String,System.String,System.Int32,System.Int32)~System.Threading.Tasks.Task{System.Int32}")]
+[assembly: SuppressMessage("Style", "IDE0074:Use compound assignment", Justification = "Does not improve readability", Scope = "member", Target = "~M:DevOpsMetrics.Core.DataAccess.TableStorage.AzureTableStorageDA.UpdateGitHubActionRunsInStorage(System.String,System.String,DevOpsMetrics.Core.Models.Common.TableStorageConfiguration,System.String,System.String,System.String,System.String,System.String,System.Int32,System.Int32)~System.Threading.Tasks.Task{System.Int32}")]
diff --git a/src/DevOpsMetrics.Core/LeadTimeForChanges.cs b/src/DevOpsMetrics.Core/LeadTimeForChanges.cs
index 1e2df776..a5461f57 100644
--- a/src/DevOpsMetrics.Core/LeadTimeForChanges.cs
+++ b/src/DevOpsMetrics.Core/LeadTimeForChanges.cs
@@ -64,32 +64,30 @@ private List> GetLeadTimeForChanges(int numberO
public static string GetLeadTimeForChangesRating(float leadTimeForChangesInHours)
{
- float dailyDeployment = 24f;
+ //float dailyDeployment = 24f;
float weeklyDeployment = 24f * 7f;
- //float monthlyDeployment = 24f * 30f;
- float sixMonthDeployment = 24f * 30f * 6f;
+ float monthlyDeployment = 24f * 30f;
+ //float sixMonthDeployment = 24f * 30f * 6f;
string rating = "";
if (leadTimeForChangesInHours <= 0f) //no rating
{
rating = "None";
}
- else if (leadTimeForChangesInHours < dailyDeployment) //less than one day
- {
- rating = "Elite";
- }
- else if (leadTimeForChangesInHours >= dailyDeployment && leadTimeForChangesInHours <= weeklyDeployment) //between one day and one week
+ else if (leadTimeForChangesInHours <= weeklyDeployment) //between one day and one week/ or once a week or faster
{
rating = "High";
}
- else if (leadTimeForChangesInHours > weeklyDeployment && leadTimeForChangesInHours <= sixMonthDeployment) //between one week and six monthes
+ else if (leadTimeForChangesInHours > weeklyDeployment && leadTimeForChangesInHours <= monthlyDeployment) //between one week and one month
{
rating = "Medium";
}
- else if (leadTimeForChangesInHours > sixMonthDeployment) //more than once every 6 months month
+ else if (leadTimeForChangesInHours > monthlyDeployment) //more than once every month
{
rating = "Low";
}
+ //no rating else statement not required here, as all scenarios are covered above with < and >
+
return rating;
}
}
diff --git a/src/DevOpsMetrics.Core/MeanTimeToRestore.cs b/src/DevOpsMetrics.Core/MeanTimeToRestore.cs
index 3695cc38..69c05b00 100644
--- a/src/DevOpsMetrics.Core/MeanTimeToRestore.cs
+++ b/src/DevOpsMetrics.Core/MeanTimeToRestore.cs
@@ -65,35 +65,29 @@ private List> GetMeanTimeToRestore(int numberOf
public static string GetMeanTimeToRestoreRating(float meanTimeToRestoreInHours)
{
- float hourlyRestoration = 1f;
- float dailyDeployment = 24f;
- float weeklyDeployment = 24f * 7f;
+ //float hourlyRestoration = 1f;
+ float dailyRestoration = 24f;
+ float weeklyRestoration = 24f * 7f;
- string rating;
+ string rating = "";
if (meanTimeToRestoreInHours <= 0)
{
rating = "None";
}
- else if (meanTimeToRestoreInHours < hourlyRestoration) //less than one hour
- {
- rating = "Elite";
- }
- else if (meanTimeToRestoreInHours < dailyDeployment) //less than one day
+ else if (meanTimeToRestoreInHours < dailyRestoration) //less than one day
{
rating = "High";
}
- else if (meanTimeToRestoreInHours < weeklyDeployment) //less than one day and one week
+ else if (meanTimeToRestoreInHours < weeklyRestoration) //less than one day and one week
{
rating = "Medium";
}
- else if (meanTimeToRestoreInHours > weeklyDeployment) //more than one week
+ else if (meanTimeToRestoreInHours > weeklyRestoration) //more than one week (originally 1 week - 1 month)
{
rating = "Low";
}
- else //no rating
- {
- rating = "None";
- }
+ //no rating else statement not required here, as all scenarios are covered above with < and >
+
return rating;
}
}
diff --git a/src/DevOpsMetrics.Core/Models/Azure/AzureStorageTableModel.cs b/src/DevOpsMetrics.Core/Models/Azure/AzureStorageTableModel.cs
index 2acbb5a2..5505f37f 100644
--- a/src/DevOpsMetrics.Core/Models/Azure/AzureStorageTableModel.cs
+++ b/src/DevOpsMetrics.Core/Models/Azure/AzureStorageTableModel.cs
@@ -1,9 +1,11 @@
-using DevOpsMetrics.Core.DataAccess.TableStorage;
-using Microsoft.Azure.Cosmos.Table;
+using System;
+using Azure;
+using Azure.Data.Tables;
+using DevOpsMetrics.Core.DataAccess.TableStorage;
namespace DevOpsMetrics.Core.Models.Azure
{
- public class AzureStorageTableModel : TableEntity
+ public class AzureStorageTableModel : ITableEntity
{
public AzureStorageTableModel(string partitionKey, string rowKey, string data)
{
@@ -21,5 +23,21 @@ public string Data
{
get; set;
}
+ public string PartitionKey
+ {
+ get; set;
+ }
+ public string RowKey
+ {
+ get; set;
+ }
+ public DateTimeOffset? Timestamp
+ {
+ get; set;
+ }
+ public ETag ETag
+ {
+ get; set;
+ }
}
}
diff --git a/src/DevOpsMetrics.Core/Models/Common/Badges.cs b/src/DevOpsMetrics.Core/Models/Common/Badges.cs
index b717f353..35770f58 100644
--- a/src/DevOpsMetrics.Core/Models/Common/Badges.cs
+++ b/src/DevOpsMetrics.Core/Models/Common/Badges.cs
@@ -24,7 +24,7 @@ public static string BadgeURL(string description, string rating)
break;
}
- //Example: https://img.shields.io/badge/Deployment%20frequency-Elite-brightgreen
+ //Example: https://img.shields.io/badge/Deployment%20frequency-High-green
return $"https://img.shields.io/badge/{description}-{rating}-{color}";
}
}
diff --git a/src/DevOpsMetrics.Core/Models/Common/ChangeFailureRateModel.cs b/src/DevOpsMetrics.Core/Models/Common/ChangeFailureRateModel.cs
index e8b17a1f..4f47dbef 100644
--- a/src/DevOpsMetrics.Core/Models/Common/ChangeFailureRateModel.cs
+++ b/src/DevOpsMetrics.Core/Models/Common/ChangeFailureRateModel.cs
@@ -51,7 +51,7 @@ public string BadgeURL
{
get
{
- //Example: https://img.shields.io/badge/Change%20failure%20rate-Elite-brightgreen
+ //Example: https://img.shields.io/badge/Change%20failure%20rate-High-green
string title = Uri.EscapeDataString("Change failure rate");
return Badges.BadgeURL(title, ChangeFailureRateMetricDescription);
}
@@ -62,7 +62,7 @@ public string BadgeWithMetricURL
get
{
string changeFailureRate = ChangeFailureRateMetric.ToString("0.00%").Replace("-", "");
- //Example: https://img.shields.io/badge/Change%20failure%20rate%20(83.33%25)-Elite-brightgreen
+ //Example: https://img.shields.io/badge/Change%20failure%20rate%20(83.33%25)-High-green
string title = Uri.EscapeDataString("Change failure rate (" + changeFailureRate + ")");
return Badges.BadgeURL(title, ChangeFailureRateMetricDescription);
}
diff --git a/src/DevOpsMetrics.Core/Models/Common/DORASummaryItem.cs b/src/DevOpsMetrics.Core/Models/Common/DORASummaryItem.cs
index 4a0422e1..c67fd2af 100644
--- a/src/DevOpsMetrics.Core/Models/Common/DORASummaryItem.cs
+++ b/src/DevOpsMetrics.Core/Models/Common/DORASummaryItem.cs
@@ -1,15 +1,54 @@
-namespace DevOpsMetrics.Core.Models.Common
+using System;
+
+namespace DevOpsMetrics.Core.Models.Common
{
public class DORASummaryItem
{
+ public DORASummaryItem()
+ {
+ }
+
+ public DORASummaryItem(string owner, string project, string repo)
+ {
+ Owner = owner;
+ Project = project;
+ Repo = repo;
+ //Set the properties to defaults (none)
+ DeploymentFrequency = 0;
+ DeploymentFrequencyBadgeURL = "https://img.shields.io/badge/Deployment%20frequency-None-lightgrey";
+ DeploymentFrequencyBadgeWithMetricURL = "https://img.shields.io/badge/Deployment%20frequency%20-None-lightgrey";
+ LeadTimeForChanges = 0;
+ LeadTimeForChangesBadgeURL = "https://img.shields.io/badge/Lead%20time%20for%20changes-None-lightgrey";
+ LeadTimeForChangesBadgeWithMetricURL = "https://img.shields.io/badge/Lead%20time%20for%20changes%20-None-lightgrey";
+ MeanTimeToRestore = 0;
+ MeanTimeToRestoreBadgeURL = "https://img.shields.io/badge/Time%20to%20restore%20service-None-lightgrey";
+ MeanTimeToRestoreBadgeWithMetricURL = "https://img.shields.io/badge/Time%20to%20restore%20service%20-None-lightgrey";
+ ChangeFailureRate = 0;
+ ChangeFailureRateBadgeURL = "https://img.shields.io/badge/Change%20failure%20rate-None-lightgrey";
+ ChangeFailureRateBadgeWithMetricURL = "https://img.shields.io/badge/Change%20failure%20rate%20-None-lightgrey";
+ LastUpdatedMessage = "No data available";
+ }
+
public string Owner
{
get; set;
}
+ public string Project
+ {
+ get; set;
+ }
public string Repo
{
get; set;
}
+ public int NumberOfDays
+ {
+ get; set;
+ }
+ public float DeploymentFrequency
+ {
+ get; set;
+ }
public string DeploymentFrequencyBadgeURL
{
get; set;
@@ -18,11 +57,19 @@ public string DeploymentFrequencyBadgeWithMetricURL
{
get; set;
}
+ public float LeadTimeForChanges
+ {
+ get; set;
+ }
public string LeadTimeForChangesBadgeURL
{
get; set;
}
- public string LeadTimeForChangesWithMetricURL
+ public string LeadTimeForChangesBadgeWithMetricURL
+ {
+ get; set;
+ }
+ public float MeanTimeToRestore
{
get; set;
}
@@ -34,6 +81,10 @@ public string MeanTimeToRestoreBadgeWithMetricURL
{
get; set;
}
+ public float ChangeFailureRate
+ {
+ get; set;
+ }
public string ChangeFailureRateBadgeURL
{
get; set;
@@ -42,5 +93,13 @@ public string ChangeFailureRateBadgeWithMetricURL
{
get; set;
}
+ public string LastUpdatedMessage
+ {
+ get; set;
+ }
+ public DateTime LastUpdated
+ {
+ get; set;
+ }
}
}
diff --git a/src/DevOpsMetrics.Core/Models/Common/DeploymentFrequencyModel.cs b/src/DevOpsMetrics.Core/Models/Common/DeploymentFrequencyModel.cs
index a2cf4b26..62320a1c 100644
--- a/src/DevOpsMetrics.Core/Models/Common/DeploymentFrequencyModel.cs
+++ b/src/DevOpsMetrics.Core/Models/Common/DeploymentFrequencyModel.cs
@@ -105,7 +105,7 @@ public string BadgeURL
{
get
{
- //Example: https://img.shields.io/badge/Deployment%20frequency-Elite-brightgreen
+ //Example: https://img.shields.io/badge/Deployment%20frequency-High-green
string title = Uri.EscapeDataString("Deployment frequency");
return Badges.BadgeURL(title, DeploymentsPerDayMetricDescription);
}
@@ -115,7 +115,7 @@ public string BadgeWithMetricURL
{
get
{
- //Example: https://img.shields.io/badge/Change%20failure%20rate%20(83.33%25)-Elite-brightgreen
+ //Example: https://img.shields.io/badge/Change%20failure%20rate%20(83.33%25)-High-green
string title = Uri.EscapeDataString("Deployment frequency (" + DeploymentsToDisplayMetric.ToString("0.00") + " " + DeploymentsToDisplayUnit + ")");
return Badges.BadgeURL(title, DeploymentsPerDayMetricDescription);
}
diff --git a/src/DevOpsMetrics.Core/Models/Common/LeadTimeForChangesModel.cs b/src/DevOpsMetrics.Core/Models/Common/LeadTimeForChangesModel.cs
index befe8f57..93541455 100644
--- a/src/DevOpsMetrics.Core/Models/Common/LeadTimeForChangesModel.cs
+++ b/src/DevOpsMetrics.Core/Models/Common/LeadTimeForChangesModel.cs
@@ -85,7 +85,7 @@ public string BadgeURL
{
get
{
- //Example: https://img.shields.io/badge/Lead%20time%20for%20changes-Elite-brightgreen
+ //Example: https://img.shields.io/badge/Lead%20time%20for%20changes-High-green
string title = Uri.EscapeDataString("Lead time for changes");
return Badges.BadgeURL(title, LeadTimeForChangesMetricDescription);
}
@@ -95,7 +95,7 @@ public string BadgeWithMetricURL
{
get
{
- //Example: https://img.shields.io/badge/Change%20failure%20rate%20(83.33%25)-Elite-brightgreen
+ //Example: https://img.shields.io/badge/Change%20failure%20rate%20(83.33%25)-High-green
string title = Uri.EscapeDataString("Lead time for changes (" + LeadTimeForChangesMetricDisplayMetric.ToString("0.0") + " " + LeadTimeForChangesMetricDisplayUnit + ")");
return Badges.BadgeURL(title, LeadTimeForChangesMetricDescription);
}
diff --git a/src/DevOpsMetrics.Core/Models/Common/MeanTimeToRestoreModel.cs b/src/DevOpsMetrics.Core/Models/Common/MeanTimeToRestoreModel.cs
index 4ee94af7..62e7d7ba 100644
--- a/src/DevOpsMetrics.Core/Models/Common/MeanTimeToRestoreModel.cs
+++ b/src/DevOpsMetrics.Core/Models/Common/MeanTimeToRestoreModel.cs
@@ -62,7 +62,7 @@ public string BadgeURL
{
get
{
- //Example: https://img.shields.io/badge/Time%20to%20restore%20service-Elite-brightgreen
+ //Example: https://img.shields.io/badge/Time%20to%20restore%20service-High-green
string title = Uri.EscapeDataString("Time to restore service");
return Badges.BadgeURL(title, MTTRAverageDurationDescription);
}
@@ -72,7 +72,7 @@ public string BadgeWithMetricURL
{
get
{
- //Example: https://img.shields.io/badge/Change%20failure%20rate%20(83.33%25)-Elite-brightgreen
+ //Example: https://img.shields.io/badge/Change%20failure%20rate%20(83.33%25)-High-green
string title = Uri.EscapeDataString("Time to restore service (" + MTTRAverageDurationInHours.ToString("0.00") + " hours)");
return Badges.BadgeURL(title, MTTRAverageDurationDescription);
}
diff --git a/src/DevOpsMetrics.Function/ProcessingResult.cs b/src/DevOpsMetrics.Core/Models/Common/ProcessingResult.cs
similarity index 85%
rename from src/DevOpsMetrics.Function/ProcessingResult.cs
rename to src/DevOpsMetrics.Core/Models/Common/ProcessingResult.cs
index aa7c90dd..a7f929b9 100644
--- a/src/DevOpsMetrics.Function/ProcessingResult.cs
+++ b/src/DevOpsMetrics.Core/Models/Common/ProcessingResult.cs
@@ -1,4 +1,4 @@
-namespace DevOpsMetrics.Function
+namespace DevOpsMetrics.Core.Models.Common
{
public class ProcessingResult
{
diff --git a/src/DevOpsMetrics.Core/Models/Common/SLAModel.cs b/src/DevOpsMetrics.Core/Models/Common/SLAModel.cs
index 6ae17986..3b2717d2 100644
--- a/src/DevOpsMetrics.Core/Models/Common/SLAModel.cs
+++ b/src/DevOpsMetrics.Core/Models/Common/SLAModel.cs
@@ -48,7 +48,7 @@ public int ItemOrder
//public string BadgeURL {
// get
// {
- // //Example: https://img.shields.io/badge/Time%20to%20restore%20service-Elite-brightgreen
+ // //Example: https://img.shields.io/badge/Time%20to%20restore%20service-High-green
// string title = Uri.EscapeDataString("SLA");
// return Badges.BadgeURL(title, MTTRAverageDurationDescription);
// }
@@ -58,7 +58,7 @@ public int ItemOrder
//{
// get
// {
- // //Example: https://img.shields.io/badge/Change%20failure%20rate%20(83.33%25)-Elite-brightgreen
+ // //Example: https://img.shields.io/badge/Change%20failure%20rate%20(83.33%25)-High-green
// string title = Uri.EscapeDataString("Time to restore service (" + MTTRAverageDurationInHours.ToString("0.00") + " hours)");
// return Badges.BadgeURL(title, MTTRAverageDurationDescription);
// }
diff --git a/src/DevOpsMetrics.Function/AzureAlertProcessor.cs b/src/DevOpsMetrics.Function/AzureAlertProcessor.cs
index 926accf4..c0fdd849 100644
--- a/src/DevOpsMetrics.Function/AzureAlertProcessor.cs
+++ b/src/DevOpsMetrics.Function/AzureAlertProcessor.cs
@@ -1,9 +1,7 @@
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
-using DevOpsMetrics.Core.DataAccess.TableStorage;
using DevOpsMetrics.Core.Models.Common;
-using DevOpsMetrics.Service.Controllers;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
@@ -29,6 +27,7 @@ public static async Task Run(
.AddUserSecrets(Assembly.GetExecutingAssembly(), true)
.AddEnvironmentVariables()
.Build();
+ ServiceApiClient serviceApiClient = new(configuration);
//Process response
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
@@ -36,9 +35,9 @@ public static async Task Run(
//save response to table
//ServiceApiClient api = new ServiceApiClient(configuration);
- SettingsController settingsController = new(configuration, new AzureTableStorageDA());
+ //SettingsController settingsController = new(configuration, new AzureTableStorageDA());
MonitoringEvent monitoringEvent = new(requestBody);
- await settingsController.UpdateDevOpsMonitoringEvent(monitoringEvent);
+ await serviceApiClient.UpdateDevOpsMonitoringEvent(monitoringEvent);
string responseMessage = "monitoring event processed successfully";
return new OkObjectResult(responseMessage);
diff --git a/src/DevOpsMetrics.Function/DevOpsMetrics.Function.csproj b/src/DevOpsMetrics.Function/DevOpsMetrics.Function.csproj
index 2f331ede..7d7c976c 100644
--- a/src/DevOpsMetrics.Function/DevOpsMetrics.Function.csproj
+++ b/src/DevOpsMetrics.Function/DevOpsMetrics.Function.csproj
@@ -1,24 +1,25 @@
-
- net7.0
- v4
- ed162474-1392-455c-b088-8b07e5438062
-
-
-
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
- Always
-
-
+
+
+ net10.0
+ v4
+ ed162474-1392-455c-b088-8b07e5438062
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ Always
+
+
\ No newline at end of file
diff --git a/src/DevOpsMetrics.Function/NightlyProcessor.cs b/src/DevOpsMetrics.Function/NightlyProcessor.cs
index eab9427a..ff2f5b21 100644
--- a/src/DevOpsMetrics.Function/NightlyProcessor.cs
+++ b/src/DevOpsMetrics.Function/NightlyProcessor.cs
@@ -2,17 +2,16 @@
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
+using Azure.Core;
using DevOpsMetrics.Core.DataAccess.TableStorage;
using DevOpsMetrics.Core.Models.AzureDevOps;
using DevOpsMetrics.Core.Models.Common;
using DevOpsMetrics.Core.Models.GitHub;
-using DevOpsMetrics.Service;
-using DevOpsMetrics.Service.Controllers;
-using Microsoft.Azure.KeyVault;
-using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
+using Azure.Core;
+using Azure.Identity;
namespace DevOpsMetrics.Function
{
@@ -38,54 +37,53 @@ public static async Task Run(
string keyVaultURL = Configuration["AppSettings:KeyVaultURL"];
string keyVaultId = Configuration["AppSettings:KeyVaultClientId"];
string keyVaultSecret = Configuration["AppSettings:KeyVaultClientSecret"];
- AzureServiceTokenProvider azureServiceTokenProvider = new();
- KeyVaultClient keyVaultClient = new(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
- builder.AddAzureKeyVault(keyVaultURL, keyVaultId, keyVaultSecret);
+ string tenantId = Configuration["AppSettings:TenantId"];
+ //AzureServiceTokenProvider azureServiceTokenProvider = new();
+ //KeyVaultClient keyVaultClient = new(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
+ //builder.AddAzureKeyVault(keyVaultURL, keyVaultId, keyVaultSecret);
+
+ if (keyVaultURL != null && keyVaultId != null && keyVaultSecret != null && tenantId != null)
+ {
+ TokenCredential tokenCredential = new ClientSecretCredential(tenantId, keyVaultId, keyVaultSecret);
+ builder.AddAzureKeyVault(new(keyVaultURL), tokenCredential);
+ }
+ else
+ {
+ throw new System.Exception("Missing configuration for Azure Key Vault");
+ }
Configuration = builder.Build();
+ ServiceApiClient serviceApiClient = new(Configuration);
//Get settings
string clientId = Configuration["AppSettings:GitHubClientId"];
string clientSecret = Configuration["AppSettings:GitHubClientSecret"];
AzureTableStorageDA azureTableStorageDA = new();
- BuildsController buildsController = new(Configuration, azureTableStorageDA);
- PullRequestsController pullRequestsController = new(Configuration, azureTableStorageDA);
- SettingsController settingsController = new(Configuration, azureTableStorageDA);
- List azSettings = settingsController.GetAzureDevOpsSettings();
- List ghSettings = settingsController.GetGitHubSettings();
- TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
+ List azSettings = await serviceApiClient.GetAzureDevOpsSettings();
+ List ghSettings = await serviceApiClient.GetGitHubSettings();
//Loop through each setting to update the runs, pull requests and pull request commits
int numberOfDays = 30;
int maxNumberOfItems = 20;
int totalResults = 0;
- foreach (AzureDevOpsSettings item in azSettings)
+ foreach (AzureDevOpsSettings azSetting in azSettings)
{
- // (int, string) buildsUpdated = (0, null);
- // (int, string) prsUpdated = (0, null);
- // try
- // {
- log.LogInformation($"Processing Azure DevOps organization {item.Organization}, project {item.Project}");
- // buildsUpdated = await api.UpdateAzureDevOpsBuilds(item.Organization, item.Project, item.Repository, item.Branch, item.BuildName, item.BuildId, numberOfDays, maxNumberOfItems);
- // prsUpdated = await api.UpdateAzureDevOpsPullRequests(item.Organization, item.Project, item.Repository, numberOfDays, maxNumberOfItems);
- // log.LogInformation($"Processed Azure DevOps organization {item.Organization}, project {item.Project}. {buildsUpdated.Item1} builds and {prsUpdated.Item1} prs/commits updated");
- // totalResults += buildsUpdated.Item1 + prsUpdated.Item1;
- // await api.UpdateAzureDevOpsProjectLog(item.Organization, item.Project, item.Repository, buildsUpdated.Item1, prsUpdated.Item1, buildsUpdated.Item2, prsUpdated.Item2, null, null);
- // }
- // catch (Exception ex)
- // {
- // string error = $"Exception while processing Azure DevOps organization {item.Organization}, project {item.Project}. {buildsUpdated.Item1} builds and {prsUpdated.Item1} prs/commits updated";
- // log.LogInformation(error);
- // await api.UpdateAzureDevOpsProjectLog(item.Organization, item.Project, item.Repository, buildsUpdated.Item1, prsUpdated.Item1, buildsUpdated.Item2, prsUpdated.Item2, ex.Message, error);
- // }
+ log.LogInformation($"Processing Azure DevOps organization {azSetting.Organization}, project {azSetting.Project}");
+ ProcessingResult ghResult = await serviceApiClient.UpdateDORASummaryItem(
+ azSetting.Organization, azSetting.Project, azSetting.Repository,
+ azSetting.Branch, azSetting.BuildName, azSetting.BuildId,
+ azSetting.ProductionResourceGroup,
+ numberOfDays, maxNumberOfItems, false);
+ totalResults = ghResult.TotalResults;
}
foreach (GitHubSettings ghSetting in ghSettings)
{
- ProcessingResult ghResult = await Processing.ProcessGitHubItem(ghSetting,
- clientId, clientSecret, tableStorageConfig,
- numberOfDays, maxNumberOfItems,
- buildsController, pullRequestsController, settingsController,
- log, totalResults);
+ log.LogInformation($"Processing GitHub owner {ghSetting.Owner}, repo {ghSetting.Repo}");
+ ProcessingResult ghResult = await serviceApiClient.UpdateDORASummaryItem(
+ ghSetting.Owner, "", ghSetting.Repo, ghSetting.Branch,
+ ghSetting.WorkflowName, ghSetting.WorkflowId,
+ ghSetting.ProductionResourceGroup,
+ numberOfDays, maxNumberOfItems, true);
totalResults = ghResult.TotalResults;
}
log.LogInformation($"C# Timer trigger function complete at: {DateTime.Now} after updating {totalResults} records");
diff --git a/src/DevOpsMetrics.Function/Processing.cs b/src/DevOpsMetrics.Function/Processing.cs
deleted file mode 100644
index d06c3cbc..00000000
--- a/src/DevOpsMetrics.Function/Processing.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using DevOpsMetrics.Core;
-using DevOpsMetrics.Core.DataAccess;
-using DevOpsMetrics.Core.DataAccess.TableStorage;
-using DevOpsMetrics.Core.Models.Common;
-using DevOpsMetrics.Core.Models.GitHub;
-using DevOpsMetrics.Service.Controllers;
-using Microsoft.Extensions.Logging;
-
-namespace DevOpsMetrics.Function
-{
- public static class Processing
- {
-
- public async static Task ProcessGitHubItem(GitHubSettings ghSetting,
- string clientId,
- string clientSecret,
- TableStorageConfiguration tableStorageConfig,
- int numberOfDays,
- int maxNumberOfItems,
- BuildsController buildsController,
- PullRequestsController pullRequestsController,
- SettingsController settingsController,
- ILogger log,
- int totalResults)
- {
- ProcessingResult result = new()
- {
- TotalResults = totalResults
- };
- try
- {
- log.LogInformation($"Processing GitHub owner {ghSetting.Owner}, repo {ghSetting.Repo}");
- result.BuildsUpdated = await buildsController.UpdateGitHubActionRuns(ghSetting.Owner, ghSetting.Repo, ghSetting.Branch, ghSetting.WorkflowName, ghSetting.WorkflowId, numberOfDays, maxNumberOfItems);
- //log.LogInformation($"Processing GitHub owner {item.Owner}, repo {item.Repo}: {buildsUpdated} builds updated");
- result.PRsUpdated = await pullRequestsController.UpdateGitHubActionPullRequests(ghSetting.Owner, ghSetting.Repo, ghSetting.Branch, numberOfDays, maxNumberOfItems);
- //log.LogInformation($"Processing GitHub owner {item.Owner}, repo {item.Repo}: {prsUpdated} pull requests updated");
- log.LogInformation($"Processed GitHub owner {ghSetting.Owner}, repo {ghSetting.Repo}. {result.BuildsUpdated} builds and {result.PRsUpdated} prs/commits updated");
- result.TotalResults += result.BuildsUpdated + result.PRsUpdated;
-
- //Process summary results for last 90 days, creating badges for the four metrics
- await UpdateSummaryMetrics(clientId, clientSecret, tableStorageConfig,
- ghSetting.Owner, ghSetting.Repo, ghSetting.Branch,
- ghSetting.WorkflowName, ghSetting.WorkflowId,
- ghSetting.ProductionResourceGroup,
- numberOfDays, maxNumberOfItems);
-
- await settingsController.UpdateGitHubProjectLog(ghSetting.Owner, ghSetting.Repo, result.BuildsUpdated, result.PRsUpdated, "", "", null, null);
- }
- catch (Exception ex)
- {
- string error = $"Exception while processing GitHub owner {ghSetting.Owner}, repo {ghSetting.Repo}. {result.BuildsUpdated} builds and {result.PRsUpdated} prs/commits updated";
- log.LogInformation(error);
- await settingsController.UpdateGitHubProjectLog(ghSetting.Owner, ghSetting.Repo, result.BuildsUpdated, result.PRsUpdated,
- ghSetting.Owner + "_" + ghSetting.Repo + "_" + ghSetting.Branch + "_" + ghSetting.WorkflowName + "_" + ghSetting.WorkflowId + "_" + numberOfDays + "_" + maxNumberOfItems,
- ghSetting.Owner + "_" + ghSetting.Repo + "_" + ghSetting.Branch + "_" + numberOfDays + "_" + maxNumberOfItems,
- ex.ToString(), error);
- }
-
- return result;
- }
-
- private static async Task UpdateSummaryMetrics(string clientId, string clientSecret,
- TableStorageConfiguration tableStorageConfig,
- string owner, string repo,
- string branch, string workflowName, string workflowId,
- string resourceGroup,
- int numberOfDays, int maxNumberOfItems,
- bool useCache = true)
- {
- //Get the DORA metrics for the last 90 days
- DeploymentFrequencyModel deploymentFrequencyModel = await DeploymentFrequencyDA.GetGitHubDeploymentFrequency(false, clientId, clientSecret, tableStorageConfig,
- owner, repo, branch, workflowName, workflowId,
- numberOfDays, maxNumberOfItems, useCache);
-
- LeadTimeForChangesModel leadTimeForChangesModel = await LeadTimeForChangesDA.GetGitHubLeadTimesForChanges(false, clientId, clientSecret, tableStorageConfig,
- owner, repo, branch, workflowName, workflowId,
- numberOfDays, maxNumberOfItems, useCache);
-
- MeanTimeToRestoreModel meanTimeToRestoreModel = new();
- if (resourceGroup != null)
- {
- meanTimeToRestoreModel = MeanTimeToRestoreDA.GetAzureMeanTimeToRestore(false, tableStorageConfig,
- DevOpsPlatform.GitHub,
- resourceGroup,
- numberOfDays, maxNumberOfItems);
- }
- else
- {
- meanTimeToRestoreModel.MTTRAverageDurationInHours = 0;
- meanTimeToRestoreModel.MTTRAverageDurationDescription = MeanTimeToRestore.GetMeanTimeToRestoreRating(0);
- }
-
- ChangeFailureRateModel changeFailureRateModel = ChangeFailureRateDA.GetChangeFailureRate(false, tableStorageConfig,
- DevOpsPlatform.GitHub,
- owner, repo, branch, workflowName,
- numberOfDays, maxNumberOfItems);
-
- //Summarize the results into a new object
- DORASummaryItem DORASummary = new()
- {
- Owner = owner,
- Repo = repo,
- DeploymentFrequencyBadgeURL = deploymentFrequencyModel.BadgeURL,
- DeploymentFrequencyBadgeWithMetricURL = deploymentFrequencyModel.BadgeWithMetricURL,
- LeadTimeForChangesBadgeURL = leadTimeForChangesModel.BadgeURL,
- LeadTimeForChangesWithMetricURL = leadTimeForChangesModel.BadgeWithMetricURL,
- MeanTimeToRestoreBadgeURL = meanTimeToRestoreModel.BadgeURL,
- MeanTimeToRestoreBadgeWithMetricURL = meanTimeToRestoreModel.BadgeWithMetricURL,
- ChangeFailureRateBadgeURL = changeFailureRateModel.BadgeURL,
- ChangeFailureRateBadgeWithMetricURL = changeFailureRateModel.BadgeWithMetricURL
- };
-
- //Serialize the summary into an Azure storage table
- await AzureTableStorageDA.UpdateDORASummaryItem(tableStorageConfig, owner, repo, DORASummary);
-
- return true;
- }
- }
-}
diff --git a/src/DevOpsMetrics.Function/ServiceApiClient.cs b/src/DevOpsMetrics.Function/ServiceApiClient.cs
index acc9ad72..aaac5fbe 100644
--- a/src/DevOpsMetrics.Function/ServiceApiClient.cs
+++ b/src/DevOpsMetrics.Function/ServiceApiClient.cs
@@ -1,130 +1,102 @@
-//using System;
-//using System.Collections.Generic;
-//using System.Diagnostics;
-//using System.Net.Http;
-//using System.Text;
-//using System.Threading.Tasks;
-//using System.Web;
-//using DevOpsMetrics.Core.Models.AzureDevOps;
-//using DevOpsMetrics.Core.Models.Common;
-//using DevOpsMetrics.Core.Models.GitHub;
-//using Microsoft.Extensions.Configuration;
-//using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using DevOpsMetrics.Core.Models.AzureDevOps;
+using DevOpsMetrics.Core.Models.Common;
+using DevOpsMetrics.Core.Models.GitHub;
+using Microsoft.Extensions.Configuration;
+using Newtonsoft.Json;
-//namespace DevOpsMetrics.Function
-//{
-// public class ServiceApiClient
-// {
-// private readonly IConfiguration Configuration;
-// private readonly HttpClient Client;
+namespace DevOpsMetrics.Function
+{
+ public class ServiceApiClient
+ {
+ private readonly IConfiguration Configuration;
+ private readonly HttpClient Client;
-// public ServiceApiClient(IConfiguration configuration)
-// {
-// Configuration = configuration;
-// Client = new HttpClient
-// {
-// BaseAddress = new Uri(Configuration["AppSettings:WebServiceURL"])
-// };
-// }
+ public ServiceApiClient(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ Client = new HttpClient
+ {
+ BaseAddress = new Uri(Configuration["AppSettings:WebServiceURL"])
+ };
+ }
-// public async Task> GetAzureDevOpsSettings()
-// {
-// string url = $"/api/Settings/GetAzureDevOpsSettings";
-// return await GetResponse>(Client, url);
-// }
+ public async Task> GetAzureDevOpsSettings()
+ {
+ string url = $"/api/Settings/GetAzureDevOpsSettings";
+ return await GetResponse>(Client, url);
+ }
-// public async Task> GetGitHubSettings()
-// {
-// string url = $"/api/Settings/GetGitHubSettings";
-// return await GetResponse>(Client, url);
-// }
+ public async Task> GetGitHubSettings()
+ {
+ string url = $"/api/Settings/GetGitHubSettings";
+ return await GetResponse>(Client, url);
+ }
-// public async Task<(int, string)> UpdateAzureDevOpsBuilds(string organization, string project, string repository, string branch, string buildName, string buildId, int numberOfDays, int maxNumberOfItems)
-// {
-// string url = $"/api/Builds/UpdateAzureDevOpsBuilds?organization={organization}&project={project}&repository={repository}&branch={branch}&buildName={buildName}&buildId={buildId}&numberOfDays={numberOfDays}&maxNumberOfItems={maxNumberOfItems}";
-// return (await GetResponse(Client, url), url);
-// }
+ public async Task UpdateDORASummaryItem(
+ string owner, string project, string repository,
+ string branch, string workflowName, string workflowId,
+ string resourceGroup, int numberOfDays, int maxNumberOfItems,
+ bool isGitHub = true)
+ {
+ string url = $"/api/DORASummary/UpdateDORASummaryItem?owner={owner}&project={project}&repository={repository}&branch={branch}&workflowName={workflowName}&workflowId={workflowId}&resourceGroup={resourceGroup}&numberOfDays={numberOfDays}&maxNumberOfItems={maxNumberOfItems}&log=&useCache=true&isGitHub={isGitHub}";
+ return await GetResponse(Client, url);
+ }
-// public async Task<(int, string)> UpdateGitHubActionRuns(string owner, string repo, string branch, string workflowName, string workflowId, int numberOfDays, int maxNumberOfItems)
-// {
-// string url = $"/api/Builds/UpdateGitHubActionRuns?owner={owner}&repo={repo}&branch={branch}&workflowName={workflowName}&workflowId={workflowId}&numberOfDays={numberOfDays}&maxNumberOfItems={maxNumberOfItems}";
-// return (await GetResponse(Client, url), url);
-// }
+ public async Task UpdateDevOpsMonitoringEvent(MonitoringEvent monitoringEvent)
+ {
+ string url = $"/api/Settings/UpdateDevOpsMonitoringEvent";
+ return await PostResponse(Client, url, monitoringEvent);
+ }
-// public async Task<(int, string)> UpdateAzureDevOpsPullRequests(string organization, string project, string repository, int numberOfDays, int maxNumberOfItems)
-// {
-// string url = $"/api/PullRequests/UpdateAzureDevOpsPullRequests?organization={organization}&project={project}&repository={repository}&numberOfDays={numberOfDays}&maxNumberOfItems={maxNumberOfItems}";
-// return (await GetResponse(Client, url), url);
-// }
+ private async Task GetResponse(HttpClient client, string url)
+ {
+ T obj = default;
+ if (client != null && url != null)
+ {
+ Debug.WriteLine("Running url: " + client.BaseAddress.ToString() + url);
+ using (HttpResponseMessage response = await client.GetAsync(url))
+ {
+ //TODO: Add a flag in the service to turn this on and off.
+ if (response.IsSuccessStatusCode == false)
+ {
+ //Log the URL to help with debugging
+ }
+ response.EnsureSuccessStatusCode();
+ string responseBody = await response.Content.ReadAsStringAsync();
+ if (string.IsNullOrEmpty(responseBody) == false)
+ {
+ obj = JsonConvert.DeserializeObject(responseBody);
+ }
+ }
+ }
+ return obj;
+ }
-// public async Task<(int, string)> UpdateGitHubActionPullRequests(string owner, string repo, string branch, int numberOfDays, int maxNumberOfItems)
-// {
-// string url = $"/api/PullRequests/UpdateGitHubActionPullRequests?owner={owner}&repo={repo}&branch={branch}&numberOfDays={numberOfDays}&maxNumberOfItems={maxNumberOfItems}";
-// return (await GetResponse(Client, url), url);
-// }
+ private async Task PostResponse(HttpClient client, string url, MonitoringEvent monitoringEvent)
+ {
-// public async Task UpdateDevOpsMonitoringEvent(string requestBody)
-// {
-// MonitoringEvent monitoringEvent = new MonitoringEvent(requestBody);
-// string url = $"/api/Settings/UpdateDevOpsMonitoringEvent";
-// return await PostResponse(Client, url, monitoringEvent);
-// }
+ if (client != null && url != null)
+ {
+ StringContent content = new StringContent(JsonConvert.SerializeObject(monitoringEvent), Encoding.UTF8, "application/json");
-// public async Task UpdateAzureDevOpsProjectLog(string organization, string project, string repository, int buildsUpdated, int prsUpdated, string buildUrl, string prUrl, string exceptionMessage, string exceptionStackTrace)
-// {
-// string url = $"/api/Settings/UpdateAzureDevOpsProjectLog?organization={organization}&project={project}&repository={repository}&buildsUpdated={buildsUpdated}&prsUpdated={prsUpdated}&buildsUpdated={buildsUpdated}&prsUpdated={prsUpdated}&buildUrl={HttpUtility.UrlEncode(buildUrl)}&prUrl={HttpUtility.UrlEncode(prUrl)}&exceptionMessage={exceptionMessage}&exceptionStackTrace={exceptionStackTrace}";
-// return await GetResponse(Client, url);
-// }
-
-// public async Task UpdateGitHubProjectLog(string owner, string repo, int buildsUpdated, int prsUpdated, string buildUrl, string prUrl, string exceptionMessage, string exceptionStackTrace)
-// {
-// string url = $"/api/Settings/UpdateGitHubProjectLog?owner={owner}&repo={repo}&buildsUpdated={buildsUpdated}&prsUpdated={prsUpdated}&buildUrl={HttpUtility.UrlEncode(buildUrl)}&prUrl={HttpUtility.UrlEncode(prUrl)}&exceptionMessage={exceptionMessage}&exceptionStackTrace={exceptionStackTrace}";
-// return await GetResponse(Client, url);
-// }
-
-// private async Task GetResponse(HttpClient client, string url)
-// {
-// T obj = default;
-// if (client != null && url != null)
-// {
-// Debug.WriteLine("Running url: " + client.BaseAddress.ToString() + url);
-// using (HttpResponseMessage response = await client.GetAsync(url))
-// {
-// //TODO: Add a flag in the service to turn this on and off.
-// if (response.IsSuccessStatusCode == false)
-// {
-// //Log the URL to help with debugging
-// }
-// response.EnsureSuccessStatusCode();
-// string responseBody = await response.Content.ReadAsStringAsync();
-// if (string.IsNullOrEmpty(responseBody) == false)
-// {
-// obj = JsonConvert.DeserializeObject(responseBody);
-// }
-// }
-// }
-// return obj;
-// }
-
-// private async Task PostResponse(HttpClient client, string url, MonitoringEvent monitoringEvent)
-// {
-
-// if (client != null && url != null)
-// {
-// StringContent content = new StringContent(JsonConvert.SerializeObject(monitoringEvent), Encoding.UTF8, "application/json");
-
-// Debug.WriteLine("Running url: " + client.BaseAddress.ToString() + url);
-// using (HttpResponseMessage response = await client.PostAsync(url, content))
-// {
-// response.EnsureSuccessStatusCode();
-// //string responseBody = await response.Content.ReadAsStringAsync();
-// //if (string.IsNullOrEmpty(responseBody) == false)
-// //{
-// // obj = JsonConvert.DeserializeObject(responseBody);
-// //}
-// }
-// }
-// return true;
-// }
-// }
-//}
+ Debug.WriteLine("Running url: " + client.BaseAddress.ToString() + url);
+ using (HttpResponseMessage response = await client.PostAsync(url, content))
+ {
+ response.EnsureSuccessStatusCode();
+ //string responseBody = await response.Content.ReadAsStringAsync();
+ //if (string.IsNullOrEmpty(responseBody) == false)
+ //{
+ // obj = JsonConvert.DeserializeObject(responseBody);
+ //}
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/src/DevOpsMetrics.FunctionalTests/DevOpsMetrics.FunctionalTests.csproj b/src/DevOpsMetrics.FunctionalTests/DevOpsMetrics.FunctionalTests.csproj
index f1f56f2a..e32e0670 100644
--- a/src/DevOpsMetrics.FunctionalTests/DevOpsMetrics.FunctionalTests.csproj
+++ b/src/DevOpsMetrics.FunctionalTests/DevOpsMetrics.FunctionalTests.csproj
@@ -1,21 +1,21 @@
- net7.0
+ net10.0
false
-
-
-
-
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
diff --git a/src/DevOpsMetrics.FunctionalTests/WebsiteSmokeTest.cs b/src/DevOpsMetrics.FunctionalTests/WebsiteSmokeTest.cs
index 7c9a81ea..3b2dd769 100644
--- a/src/DevOpsMetrics.FunctionalTests/WebsiteSmokeTest.cs
+++ b/src/DevOpsMetrics.FunctionalTests/WebsiteSmokeTest.cs
@@ -145,7 +145,7 @@ public void GotoWebHomeProjectDevOpsMetricsPageTest()
bool webLoaded;
//Act
- string webURL = _webUrl + "Home//Project?projectId=samsmithnz_DevOpsMetrics";
+ string webURL = _webUrl + "Home//Project?projectId=DeveloperMetrics_DevOpsMetrics";
Console.WriteLine("webURL:" + webURL);
_driver.Navigate().GoToUrl(webURL);
webLoaded = (_driver.Url == webURL);
diff --git a/src/DevOpsMetrics.Service/Controllers/BuildsController.cs b/src/DevOpsMetrics.Service/Controllers/BuildsController.cs
index 6ea8bf85..d1984439 100644
--- a/src/DevOpsMetrics.Service/Controllers/BuildsController.cs
+++ b/src/DevOpsMetrics.Service/Controllers/BuildsController.cs
@@ -2,7 +2,6 @@
using System.Threading.Tasks;
using DevOpsMetrics.Core.DataAccess.TableStorage;
using DevOpsMetrics.Core.Models.Common;
-using DevOpsMetrics.Service.Utility;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
@@ -35,12 +34,11 @@ public async Task UpdateAzureDevOpsBuilds(
//Get the PAT token from the key vault
string patTokenName = PartitionKeys.CreateAzureDevOpsSettingsPartitionKeyPatToken(organization, project, repository);
- patTokenName = SecretsProcessing.CleanKey(patTokenName);
string patToken = Configuration[patTokenName];
- if (string.IsNullOrEmpty(patToken) == true)
- {
- throw new Exception($"patToken '{patTokenName}' not found in key vault");
- }
+ //if (string.IsNullOrEmpty(patToken))
+ //{
+ // throw new Exception($"patToken '{patTokenName}' not found in key vault");
+ //}
numberOfRecordsSaved = await AzureTableStorageDA.UpdateAzureDevOpsBuildsInStorage(patToken, tableStorageConfig, organization, project, branch, buildName, buildId, numberOfDays, maxNumberOfItems);
}
@@ -71,15 +69,13 @@ public async Task UpdateGitHubActionRuns(
//Get the client id and secret from the settings
string clientIdName = PartitionKeys.CreateGitHubSettingsPartitionKeyClientId(owner, repo);
- clientIdName = SecretsProcessing.CleanKey(clientIdName);
string clientSecretName = PartitionKeys.CreateGitHubSettingsPartitionKeyClientSecret(owner, repo);
- clientSecretName = SecretsProcessing.CleanKey(clientSecretName);
string clientId = Configuration[clientIdName];
string clientSecret = Configuration[clientSecretName];
- if (string.IsNullOrEmpty(clientId) == true | string.IsNullOrEmpty(clientSecret) == true)
- {
- throw new Exception($"clientId '{clientId}' or clientSecret '{clientSecret}' not found in key vault");
- }
+ //if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret))
+ //{
+ // throw new Exception($"clientId '{clientId}' or clientSecret '{clientSecret}' not found in key vault");
+ //}
numberOfRecordsSaved = await AzureTableStorageDA.UpdateGitHubActionRunsInStorage(clientId, clientSecret, tableStorageConfig,
owner, repo, branch, workflowName, workflowId, numberOfDays, maxNumberOfItems);
diff --git a/src/DevOpsMetrics.Service/Controllers/ChangeFailureRateController.cs b/src/DevOpsMetrics.Service/Controllers/ChangeFailureRateController.cs
index 6c59e316..39bf2e9a 100644
--- a/src/DevOpsMetrics.Service/Controllers/ChangeFailureRateController.cs
+++ b/src/DevOpsMetrics.Service/Controllers/ChangeFailureRateController.cs
@@ -18,12 +18,12 @@ public ChangeFailureRateController(IConfiguration configuration)
}
[HttpGet("GetChangeFailureRate")]
- public ChangeFailureRateModel GetChangeFailureRate(bool getSampleData,
+ public async Task GetChangeFailureRate(bool getSampleData,
DevOpsPlatform targetDevOpsPlatform, string organization_owner, string project_repo, string branch, string buildName_workflowName,
int numberOfDays, int maxNumberOfItems)
{
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
- ChangeFailureRateModel model = ChangeFailureRateDA.GetChangeFailureRate(getSampleData, tableStorageConfig, targetDevOpsPlatform,
+ ChangeFailureRateModel model = await ChangeFailureRateDA.GetChangeFailureRate(getSampleData, tableStorageConfig, targetDevOpsPlatform,
organization_owner, project_repo, branch, buildName_workflowName,
numberOfDays, maxNumberOfItems);
return model;
diff --git a/src/DevOpsMetrics.Service/Controllers/DORASummaryController.cs b/src/DevOpsMetrics.Service/Controllers/DORASummaryController.cs
index 09aa8c8d..f677b69d 100644
--- a/src/DevOpsMetrics.Service/Controllers/DORASummaryController.cs
+++ b/src/DevOpsMetrics.Service/Controllers/DORASummaryController.cs
@@ -1,7 +1,13 @@
-using DevOpsMetrics.Core.DataAccess;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using DevOpsMetrics.Core;
+using DevOpsMetrics.Core.DataAccess;
+using DevOpsMetrics.Core.DataAccess.TableStorage;
using DevOpsMetrics.Core.Models.Common;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
namespace DevOpsMetrics.Service.Controllers
{
@@ -18,13 +24,402 @@ public DORASummaryController(IConfiguration configuration)
// Get DORA Summary Items
[HttpGet("GetDORASummaryItems")]
- public DORASummaryItem GetDORASummaryItems(string owner, string repository)
+ public async Task> GetDORASummaryItems(string owner)
{
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
- DORASummaryItem model = DORASummaryDA.GetDORASummaryItem(tableStorageConfig, owner, repository);
+ List model = await DORASummaryDA.GetDORASummaryItems(tableStorageConfig, owner);
+ return model;
+ }
+ // Get DORA Summary Item
+ [HttpGet("GetDORASummaryItem")]
+ public async Task GetDORASummaryItem(string owner, string project, string repo)
+ {
+ TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
+ DORASummaryItem model = await DORASummaryDA.GetDORASummaryItem(tableStorageConfig, owner, project, repo);
return model;
}
+ [HttpGet("UpdateDORASummaryItem")]
+ public async Task UpdateDORASummaryItem(
+ string owner,
+ string project,
+ string repo,
+ string branch,
+ string workflowName,
+ string workflowId,
+ string resourceGroup,
+ int numberOfDays,
+ int maxNumberOfItems,
+ Microsoft.Extensions.Logging.ILogger log = null,
+ bool useCache = true,
+ bool isGitHub = true,
+ bool useParallelProcessing = true)
+ {
+ //Start timer
+ DateTime startTime = DateTime.Now;
+ AzureTableStorageDA azureTableStorageDA = new();
+ TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
+ string clientId = null;
+ string clientSecret = null;
+ string patToken = null;
+ //Instead of throwing exceptions when there are no secrets, add the error message to the overall processing message
+ string errorMessage = null;
+ if (isGitHub == true)
+ {
+ //Get the client id and secret from the settings
+ string clientIdName = PartitionKeys.CreateGitHubSettingsPartitionKeyClientId(owner, repo);
+ string clientSecretName = PartitionKeys.CreateGitHubSettingsPartitionKeyClientSecret(owner, repo);
+ clientId = Configuration[clientIdName];
+ clientSecret = Configuration[clientSecretName];
+ if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret))
+ {
+ errorMessage = $"clientId '{clientIdName}' or clientSecret '{clientSecretName}' not found in key vault";
+ }
+ }
+ else
+ {
+ string patTokenName = PartitionKeys.CreateAzureDevOpsSettingsPartitionKeyPatToken(owner, project, repo);
+ patToken = Configuration[patTokenName];
+ if (string.IsNullOrEmpty(patToken))
+ {
+ errorMessage = $"patToken '{patTokenName}' not found in key vault";
+ }
+ }
+
+ ProcessingResult result = new();
+ //If the settings are missing, skip the processing and return an empty result
+ if (string.IsNullOrEmpty(errorMessage) == false)
+ {
+ if (log == null)
+ {
+ Console.WriteLine(errorMessage);
+ }
+ else
+ {
+ log.LogInformation(errorMessage);
+ }
+ return result;
+ }
+ DeploymentFrequencyModel deploymentFrequencyModel = new();
+ LeadTimeForChangesModel leadTimeForChangesModel = new();
+ MeanTimeToRestoreModel meanTimeToRestoreModel = new();
+ ChangeFailureRateModel changeFailureRateModel = new();
+ try
+ {
+ string message = "";
+ if (isGitHub == true)
+ {
+ message = $"Processing GitHub owner {owner}, repo {repo}";
+ }
+ else
+ {
+ message = $"Processing Azure DevOps organization {owner}, project {project}, repo {repo}";
+ }
+ //TODO: fix this - should be using a common interface, not this null hack
+ if (log == null)
+ {
+ Console.WriteLine(message);
+ }
+ else
+ {
+ log.LogInformation(message);
+ }
+ if (useParallelProcessing)
+ {
+ //Call the builds and prs in parallel
+ Task buildTask;
+ Task prTask;
+ if (isGitHub == true)
+ {
+ //result.BuildsUpdated = await
+ buildTask = azureTableStorageDA.UpdateGitHubActionRunsInStorage(clientId, clientSecret, tableStorageConfig,
+ owner, repo, branch, workflowName, workflowId, numberOfDays, maxNumberOfItems);
+ //result.PRsUpdated = await
+ prTask = azureTableStorageDA.UpdateGitHubActionPullRequestsInStorage(clientId, clientSecret, tableStorageConfig,
+ owner, repo, branch, numberOfDays, maxNumberOfItems);
+ message = $"Processed GitHub owner {owner}, repo {repo}. {result.BuildsUpdated} builds and {result.PRsUpdated} prs/commits updated";
+ }
+ else
+ {
+ //result.BuildsUpdated = await
+ buildTask = azureTableStorageDA.UpdateAzureDevOpsBuildsInStorage(patToken, tableStorageConfig,
+ owner, repo, branch, workflowName, workflowId, numberOfDays, maxNumberOfItems);
+ //result.PRsUpdated = await
+ prTask = azureTableStorageDA.UpdateAzureDevOpsPullRequestsInStorage(patToken, tableStorageConfig,
+ owner, repo, branch, numberOfDays, maxNumberOfItems);
+ message = $"Processed Azure DevOps organization {owner}, project {project}, repo {repo}. {result.BuildsUpdated} builds and {result.PRsUpdated} prs/commits updated";
+ }
+ await Task.WhenAll(buildTask, prTask);
+ result.BuildsUpdated = await buildTask;
+ result.PRsUpdated = await prTask;
+ }
+ else
+ {
+ if (isGitHub == true)
+ {
+ result.BuildsUpdated = await azureTableStorageDA.UpdateGitHubActionRunsInStorage(clientId, clientSecret, tableStorageConfig,
+ owner, repo, branch, workflowName, workflowId, numberOfDays, maxNumberOfItems);
+ result.PRsUpdated = await azureTableStorageDA.UpdateGitHubActionPullRequestsInStorage(clientId, clientSecret, tableStorageConfig,
+ owner, repo, branch, numberOfDays, maxNumberOfItems);
+ message = $"Processed GitHub owner {owner}, repo {repo}. {result.BuildsUpdated} builds and {result.PRsUpdated} prs/commits updated";
+ }
+ else
+ {
+ result.BuildsUpdated = await azureTableStorageDA.UpdateAzureDevOpsBuildsInStorage(patToken, tableStorageConfig,
+ owner, repo, branch, workflowName, workflowId, numberOfDays, maxNumberOfItems);
+ result.PRsUpdated = await azureTableStorageDA.UpdateAzureDevOpsPullRequestsInStorage(patToken, tableStorageConfig,
+ owner, repo, branch, numberOfDays, maxNumberOfItems);
+ message = $"Processed Azure DevOps organization {owner}, project {project}, repo {repo}. {result.BuildsUpdated} builds and {result.PRsUpdated} prs/commits updated";
+ }
+ }
+ if (log == null)
+ {
+ Console.WriteLine(message);
+ }
+ else
+ {
+ log.LogInformation(message);
+ }
+ result.TotalResults += result.BuildsUpdated + result.PRsUpdated;
+
+ //Process summary results for last 90 days, creating badges for the four metrics
+ //Get the DORA metrics for the last 90 days
+ if (useParallelProcessing)
+ {
+ Task deploymentFrequencyTask;
+ Task leadTimeForChangesTask;
+ Task meanTimeToRestoreTask;
+ Task changeFailureRateTask;
+
+ //Get the deployment frequency and lead time for changes in parallel
+ if (isGitHub == true)
+ {
+ deploymentFrequencyTask = DeploymentFrequencyDA.GetGitHubDeploymentFrequency(false, clientId, clientSecret, tableStorageConfig,
+ owner, repo, branch, workflowName, workflowId,
+ numberOfDays, maxNumberOfItems, useCache);
+
+ leadTimeForChangesTask = LeadTimeForChangesDA.GetGitHubLeadTimesForChanges(false, clientId, clientSecret, tableStorageConfig,
+ owner, repo, branch, workflowName, workflowId,
+ numberOfDays, maxNumberOfItems, useCache);
+ }
+ else
+ {
+ deploymentFrequencyTask = DeploymentFrequencyDA.GetAzureDevOpsDeploymentFrequency(false, patToken, tableStorageConfig,
+ owner, project, branch, workflowName,
+ numberOfDays, maxNumberOfItems, useCache);
+
+ leadTimeForChangesTask = LeadTimeForChangesDA.GetAzureDevOpsLeadTimesForChanges(false, patToken, tableStorageConfig,
+ owner, project, repo, branch, workflowName,
+ numberOfDays, maxNumberOfItems, useCache);
+ }
+ //Get the mean time to restore and change failure rate in parallel
+ if (resourceGroup != null)
+ {
+ meanTimeToRestoreTask = MeanTimeToRestoreDA.GetAzureMeanTimeToRestore(false, tableStorageConfig,
+ DevOpsPlatform.GitHub,
+ resourceGroup,
+ numberOfDays, maxNumberOfItems);
+ }
+ else
+ {
+ meanTimeToRestoreTask = null;
+ meanTimeToRestoreModel.MTTRAverageDurationInHours = 0;
+ meanTimeToRestoreModel.MTTRAverageDurationDescription = MeanTimeToRestore.GetMeanTimeToRestoreRating(0);
+ }
+ changeFailureRateTask = ChangeFailureRateDA.GetChangeFailureRate(false, tableStorageConfig,
+ DevOpsPlatform.GitHub,
+ owner, repo, branch, workflowName,
+ numberOfDays, maxNumberOfItems);
+
+ //Process the tasks in parallel
+ if (meanTimeToRestoreTask != null)
+ {
+ await Task.WhenAll(deploymentFrequencyTask, leadTimeForChangesTask, meanTimeToRestoreTask, changeFailureRateTask);
+ }
+ else
+ {
+ await Task.WhenAll(deploymentFrequencyTask, leadTimeForChangesTask, changeFailureRateTask);
+ }
+ deploymentFrequencyModel = await deploymentFrequencyTask;
+ leadTimeForChangesModel = await leadTimeForChangesTask;
+ if (meanTimeToRestoreTask != null)
+ {
+ meanTimeToRestoreModel = await meanTimeToRestoreTask;
+ }
+ changeFailureRateModel = await changeFailureRateTask;
+ }
+ else
+ {
+ if (isGitHub == true)
+ {
+ //Get the deployment frequency and lead time for changes
+ deploymentFrequencyModel = await DeploymentFrequencyDA.GetGitHubDeploymentFrequency(false, clientId, clientSecret, tableStorageConfig,
+ owner, repo, branch, workflowName, workflowId,
+ numberOfDays, maxNumberOfItems, useCache);
+
+ leadTimeForChangesModel = await LeadTimeForChangesDA.GetGitHubLeadTimesForChanges(false, clientId, clientSecret, tableStorageConfig,
+ owner, repo, branch, workflowName, workflowId,
+ numberOfDays, maxNumberOfItems, useCache);
+ }
+ else
+ {
+ deploymentFrequencyModel = await DeploymentFrequencyDA.GetAzureDevOpsDeploymentFrequency(false, patToken, tableStorageConfig,
+ owner, project, branch, workflowName,
+ numberOfDays, maxNumberOfItems, useCache);
+
+ leadTimeForChangesModel = await LeadTimeForChangesDA.GetAzureDevOpsLeadTimesForChanges(false, patToken, tableStorageConfig,
+ owner, project, repo, branch, workflowName,
+ numberOfDays, maxNumberOfItems, useCache);
+ }
+ //Get the mean time to restore and change failure rate
+ if (resourceGroup != null)
+ {
+ meanTimeToRestoreModel = await MeanTimeToRestoreDA.GetAzureMeanTimeToRestore(false, tableStorageConfig,
+ DevOpsPlatform.GitHub,
+ resourceGroup,
+ numberOfDays, maxNumberOfItems);
+ }
+ else
+ {
+ meanTimeToRestoreModel.MTTRAverageDurationInHours = 0;
+ meanTimeToRestoreModel.MTTRAverageDurationDescription = MeanTimeToRestore.GetMeanTimeToRestoreRating(0);
+ }
+ changeFailureRateModel = await ChangeFailureRateDA.GetChangeFailureRate(false, tableStorageConfig,
+ DevOpsPlatform.GitHub,
+ owner, repo, branch, workflowName,
+ numberOfDays, maxNumberOfItems);
+ }
+
+ //Get the total time since startTime
+ string processingLogMessage = $"Processed summary for {owner}, repo {repo} in {(DateTime.Now - startTime).TotalSeconds} seconds";
+ if (errorMessage != null)
+ {
+ processingLogMessage += $", error: {errorMessage}";
+ }
+
+ //Summarize the results into a new object
+ DORASummaryItem DORASummary = new()
+ {
+ Owner = owner,
+ Project = project,
+ Repo = repo,
+ NumberOfDays = numberOfDays,
+ DeploymentFrequency = deploymentFrequencyModel.DeploymentsPerDayMetric,
+ DeploymentFrequencyBadgeURL = deploymentFrequencyModel.BadgeURL,
+ DeploymentFrequencyBadgeWithMetricURL = deploymentFrequencyModel.BadgeWithMetricURL,
+ LeadTimeForChanges = leadTimeForChangesModel.LeadTimeForChangesMetric,
+ LeadTimeForChangesBadgeURL = leadTimeForChangesModel.BadgeURL,
+ LeadTimeForChangesBadgeWithMetricURL = leadTimeForChangesModel.BadgeWithMetricURL,
+ MeanTimeToRestore = meanTimeToRestoreModel.MTTRAverageDurationInHours,
+ MeanTimeToRestoreBadgeURL = meanTimeToRestoreModel.BadgeURL,
+ MeanTimeToRestoreBadgeWithMetricURL = meanTimeToRestoreModel.BadgeWithMetricURL,
+ ChangeFailureRate = changeFailureRateModel.ChangeFailureRateMetric,
+ ChangeFailureRateBadgeURL = changeFailureRateModel.BadgeURL,
+ ChangeFailureRateBadgeWithMetricURL = changeFailureRateModel.BadgeWithMetricURL,
+ LastUpdatedMessage = processingLogMessage,
+ LastUpdated = DateTime.Now
+ };
+
+ //Serialize the summary into an Azure storage table
+ await AzureTableStorageDA.UpdateDORASummaryItem(tableStorageConfig, owner, project, repo, DORASummary);
+
+ //await settingsController.UpdateGitHubProjectLog(ghSetting.Owner, ghSetting.Repo, result.BuildsUpdated, result.PRsUpdated, "", "", null, null);
+ ProjectLog projectLog = null;
+ if (isGitHub == true)
+ {
+ projectLog = new(
+ PartitionKeys.CreateGitHubSettingsPartitionKey(owner, repo),
+ result.BuildsUpdated, result.PRsUpdated,
+ "", "", null, null);
+ }
+ else
+ {
+ projectLog = new(
+ PartitionKeys.CreateAzureDevOpsSettingsPartitionKey(owner, project, repo),
+ result.BuildsUpdated, result.PRsUpdated,
+ "", "", null, null);
+ }
+ if (projectLog != null)
+ {
+ await azureTableStorageDA.UpdateProjectLogInStorage(tableStorageConfig, projectLog);
+ }
+ }
+ catch (Exception ex)
+ {
+ string error = $"Exception while processing GitHub owner {owner}, repo {repo}. {result.BuildsUpdated} builds and {result.PRsUpdated} prs/commits updated " + ex.ToString();
+ if (log == null)
+ {
+ Console.WriteLine(error);
+ }
+ else
+ {
+ log.LogInformation(error);
+ }
+ ProjectLog projectLog;
+ if (isGitHub == true)
+ {
+ projectLog = new(
+ PartitionKeys.CreateGitHubSettingsPartitionKey(owner, repo),
+ result.BuildsUpdated, result.PRsUpdated,
+ owner + "_" + repo + "_" + branch + "_" + workflowName + "_" + workflowId + "_" + numberOfDays + "_" + maxNumberOfItems,
+ owner + "_" + repo + "_" + branch + "_" + numberOfDays + "_" + maxNumberOfItems,
+ ex.ToString(), error);
+ }
+ else
+ {
+ projectLog = new(
+ PartitionKeys.CreateAzureDevOpsSettingsPartitionKey(owner, project, repo),
+ result.BuildsUpdated, result.PRsUpdated,
+ owner + "_" + project + "_" + repo + "_" + branch + "_" + workflowName + "_" + workflowId + "_" + numberOfDays + "_" + maxNumberOfItems,
+ owner + "_" + project + "_" + repo + "_" + branch + "_" + numberOfDays + "_" + maxNumberOfItems,
+ ex.ToString(), error);
+ }
+ //Update the DORA object with the error message
+ try
+ {
+ //Get the total time since startTime
+ string processingLogMessage = $"Error processing summary for {owner}, repo {repo} in {(DateTime.Now - startTime).TotalSeconds} seconds";
+
+ //Summarize the results into a new object
+ DORASummaryItem DORASummary = new()
+ {
+ Owner = owner,
+ Project = project,
+ Repo = repo,
+ NumberOfDays = numberOfDays,
+ DeploymentFrequency = deploymentFrequencyModel.DeploymentsPerDayMetric,
+ DeploymentFrequencyBadgeURL = deploymentFrequencyModel.BadgeURL,
+ DeploymentFrequencyBadgeWithMetricURL = deploymentFrequencyModel.BadgeWithMetricURL,
+ LeadTimeForChanges = leadTimeForChangesModel.LeadTimeForChangesMetric,
+ LeadTimeForChangesBadgeURL = leadTimeForChangesModel.BadgeURL,
+ LeadTimeForChangesBadgeWithMetricURL = leadTimeForChangesModel.BadgeWithMetricURL,
+ MeanTimeToRestore = meanTimeToRestoreModel.MTTRAverageDurationInHours,
+ MeanTimeToRestoreBadgeURL = meanTimeToRestoreModel.BadgeURL,
+ MeanTimeToRestoreBadgeWithMetricURL = meanTimeToRestoreModel.BadgeWithMetricURL,
+ ChangeFailureRate = changeFailureRateModel.ChangeFailureRateMetric,
+ ChangeFailureRateBadgeURL = changeFailureRateModel.BadgeURL,
+ ChangeFailureRateBadgeWithMetricURL = changeFailureRateModel.BadgeWithMetricURL,
+ LastUpdatedMessage = processingLogMessage,
+ LastUpdated = DateTime.Now
+ };
+ //Serialize the summary into an Azure storage table
+ await AzureTableStorageDA.UpdateDORASummaryItem(tableStorageConfig, owner, project, repo, DORASummary);
+ }
+ catch
+ {
+ //Do nothing, we handled the error above
+ }
+
+ if (projectLog != null)
+ {
+ await azureTableStorageDA.UpdateProjectLogInStorage(tableStorageConfig, projectLog);
+ }
+ //throw new Exception(error, ex);
+ }
+
+ return result;
+ }
+
}
}
\ No newline at end of file
diff --git a/src/DevOpsMetrics.Service/Controllers/DeploymentFrequencyController.cs b/src/DevOpsMetrics.Service/Controllers/DeploymentFrequencyController.cs
index 8d0fe966..6c6f925f 100644
--- a/src/DevOpsMetrics.Service/Controllers/DeploymentFrequencyController.cs
+++ b/src/DevOpsMetrics.Service/Controllers/DeploymentFrequencyController.cs
@@ -35,10 +35,10 @@ public async Task GetAzureDevOpsDeploymentFrequency(bo
string patTokenName = PartitionKeys.CreateAzureDevOpsSettingsPartitionKeyPatToken(organization, project, repository);
patTokenName = SecretsProcessing.CleanKey(patTokenName);
string patToken = Configuration[patTokenName];
- if (string.IsNullOrEmpty(patToken) == true)
- {
- throw new Exception($"patToken '{patTokenName}' not found in key vault");
- }
+ //if (string.IsNullOrEmpty(patToken))
+ //{
+ // throw new Exception($"patToken '{patTokenName}' not found in key vault");
+ //}
DeploymentFrequencyDA da = new();
model = await DeploymentFrequencyDA.GetAzureDevOpsDeploymentFrequency(getSampleData, patToken, tableStorageConfig, organization, project, branch, buildName, numberOfDays, maxNumberOfItems, useCache);
@@ -76,10 +76,10 @@ public async Task GetGitHubDeploymentFrequency(bool ge
clientSecretName = SecretsProcessing.CleanKey(clientSecretName);
string clientId = Configuration[clientIdName];
string clientSecret = Configuration[clientSecretName];
- if (string.IsNullOrEmpty(clientId) == true | string.IsNullOrEmpty(clientSecret) == true)
- {
- throw new Exception($"clientId '{clientId}' or clientSecret '{clientSecret}' not found in key vault");
- }
+ //if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret))
+ //{
+ // throw new Exception($"clientId '{clientId}' or clientSecret '{clientSecret}' not found in key vault");
+ //}
DeploymentFrequencyDA da = new();
model = await DeploymentFrequencyDA.GetGitHubDeploymentFrequency(getSampleData, clientId, clientSecret, tableStorageConfig, owner, repo, branch, workflowName, workflowId, numberOfDays, maxNumberOfItems, useCache);
diff --git a/src/DevOpsMetrics.Service/Controllers/LeadTimeForChangesController.cs b/src/DevOpsMetrics.Service/Controllers/LeadTimeForChangesController.cs
index 9cde7844..788804bd 100644
--- a/src/DevOpsMetrics.Service/Controllers/LeadTimeForChangesController.cs
+++ b/src/DevOpsMetrics.Service/Controllers/LeadTimeForChangesController.cs
@@ -33,12 +33,11 @@ public async Task GetAzureDevOpsLeadTimeForChanges(bool
//Get the PAT token from the key vault
string patTokenName = PartitionKeys.CreateAzureDevOpsSettingsPartitionKeyPatToken(organization, project, repository);
- patTokenName = SecretsProcessing.CleanKey(patTokenName);
string patToken = Configuration[patTokenName];
- if (string.IsNullOrEmpty(patToken) == true)
- {
- throw new Exception($"patToken '{patTokenName}' not found in key vault");
- }
+ //if (string.IsNullOrEmpty(patToken))
+ //{
+ // throw new Exception($"patToken '{patTokenName}' not found in key vault");
+ //}
LeadTimeForChangesDA da = new();
model = await LeadTimeForChangesDA.GetAzureDevOpsLeadTimesForChanges(getSampleData, patToken, tableStorageConfig,
@@ -77,10 +76,10 @@ public async Task GetGitHubLeadTimeForChanges(bool getS
clientSecretName = SecretsProcessing.CleanKey(clientSecretName);
string clientId = Configuration[clientIdName];
string clientSecret = Configuration[clientSecretName];
- if (string.IsNullOrEmpty(clientId) == true | string.IsNullOrEmpty(clientSecret) == true)
- {
- throw new Exception($"clientId '{clientId}' or clientSecret '{clientSecret}' not found in key vault");
- }
+ //if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret))
+ //{
+ // throw new Exception($"clientId '{clientId}' or clientSecret '{clientSecret}' not found in key vault");
+ //}
LeadTimeForChangesDA da = new();
model = await LeadTimeForChangesDA.GetGitHubLeadTimesForChanges(getSampleData, clientId, clientSecret, tableStorageConfig,
diff --git a/src/DevOpsMetrics.Service/Controllers/MeanTimeToRestoreController.cs b/src/DevOpsMetrics.Service/Controllers/MeanTimeToRestoreController.cs
index b636b44d..f0d54996 100644
--- a/src/DevOpsMetrics.Service/Controllers/MeanTimeToRestoreController.cs
+++ b/src/DevOpsMetrics.Service/Controllers/MeanTimeToRestoreController.cs
@@ -1,4 +1,5 @@
-using DevOpsMetrics.Core.DataAccess;
+using System.Threading.Tasks;
+using DevOpsMetrics.Core.DataAccess;
using DevOpsMetrics.Core.Models.Common;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
@@ -17,12 +18,12 @@ public MeanTimeToRestoreController(IConfiguration configuration)
}
[HttpGet("GetAzureMeanTimeToRestore")]
- public MeanTimeToRestoreModel GetAzureMeanTimeToRestore(bool getSampleData,
+ public async Task GetAzureMeanTimeToRestore(bool getSampleData,
DevOpsPlatform targetDevOpsPlatform, string resourceGroup,
int numberOfDays, int maxNumberOfItems)
{
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
- MeanTimeToRestoreModel model = MeanTimeToRestoreDA.GetAzureMeanTimeToRestore(getSampleData, tableStorageConfig,
+ MeanTimeToRestoreModel model = await MeanTimeToRestoreDA.GetAzureMeanTimeToRestore(getSampleData, tableStorageConfig,
targetDevOpsPlatform, resourceGroup,
numberOfDays, maxNumberOfItems);
return model;
diff --git a/src/DevOpsMetrics.Service/Controllers/PullRequestsController.cs b/src/DevOpsMetrics.Service/Controllers/PullRequestsController.cs
index 62fff012..672d95aa 100644
--- a/src/DevOpsMetrics.Service/Controllers/PullRequestsController.cs
+++ b/src/DevOpsMetrics.Service/Controllers/PullRequestsController.cs
@@ -35,10 +35,10 @@ public async Task UpdateAzureDevOpsPullRequests(
string patTokenName = PartitionKeys.CreateAzureDevOpsSettingsPartitionKeyPatToken(organization, project, repository);
patTokenName = SecretsProcessing.CleanKey(patTokenName);
string patToken = Configuration[patTokenName];
- if (string.IsNullOrEmpty(patToken) == true)
- {
- throw new Exception($"patToken '{patTokenName}' not found in key vault");
- }
+ //if (string.IsNullOrEmpty(patToken))
+ //{
+ // throw new Exception($"patToken '{patTokenName}' not found in key vault");
+ //}
numberOfRecordsSaved = await AzureTableStorageDA.UpdateAzureDevOpsPullRequestsInStorage(patToken, tableStorageConfig,
organization, project, repository, numberOfDays, maxNumberOfItems);
@@ -74,10 +74,10 @@ public async Task UpdateGitHubActionPullRequests(
clientSecretName = SecretsProcessing.CleanKey(clientSecretName);
string clientId = Configuration[clientIdName];
string clientSecret = Configuration[clientSecretName];
- if (string.IsNullOrEmpty(clientId) == true | string.IsNullOrEmpty(clientSecret) == true)
- {
- throw new Exception($"clientId '{clientId}' or clientSecret '{clientSecret}' not found in key vault");
- }
+ //if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret))
+ //{
+ // throw new Exception($"clientId '{clientId}' or clientSecret '{clientSecret}' not found in key vault");
+ //}
numberOfRecordsSaved = await AzureTableStorageDA.UpdateGitHubActionPullRequestsInStorage(clientId, clientSecret, tableStorageConfig,
owner, repo, branch, numberOfDays, maxNumberOfItems);
@@ -110,10 +110,10 @@ public async Task UpdateAzureDevOpsPullRequestCommits(
string patTokenName = PartitionKeys.CreateAzureDevOpsSettingsPartitionKeyPatToken(organization, project, repository);
patTokenName = SecretsProcessing.CleanKey(patTokenName);
string patToken = Configuration[patTokenName];
- if (string.IsNullOrEmpty(patToken) == true)
- {
- throw new Exception($"patToken '{patTokenName}' not found in key vault");
- }
+ //if (string.IsNullOrEmpty(patToken))
+ //{
+ // throw new Exception($"patToken '{patTokenName}' not found in key vault");
+ //}
numberOfRecordsSaved = await AzureTableStorageDA.UpdateAzureDevOpsPullRequestCommitsInStorage(patToken, tableStorageConfig,
organization, project, repository, pullRequestId, numberOfDays, maxNumberOfItems);
@@ -148,10 +148,10 @@ public async Task UpdateGitHubActionPullRequestCommits(
clientSecretName = SecretsProcessing.CleanKey(clientSecretName);
string clientId = Configuration[clientIdName];
string clientSecret = Configuration[clientSecretName];
- if (string.IsNullOrEmpty(clientId) == true | string.IsNullOrEmpty(clientSecret) == true)
- {
- throw new Exception($"clientId '{clientId}' or clientSecret '{clientSecret}' not found in key vault");
- }
+ //if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret))
+ //{
+ // throw new Exception($"clientId '{clientId}' or clientSecret '{clientSecret}' not found in key vault");
+ //}
numberOfRecordsSaved = await AzureTableStorageDA.UpdateGitHubActionPullRequestCommitsInStorage(clientId, clientSecret, tableStorageConfig,
owner, repo, pull_number);
diff --git a/src/DevOpsMetrics.Service/Controllers/SettingsController.cs b/src/DevOpsMetrics.Service/Controllers/SettingsController.cs
index e9e3b8fa..ebbfefbb 100644
--- a/src/DevOpsMetrics.Service/Controllers/SettingsController.cs
+++ b/src/DevOpsMetrics.Service/Controllers/SettingsController.cs
@@ -28,18 +28,28 @@ public SettingsController(IConfiguration configuration, IAzureTableStorageDA azu
}
[HttpGet("GetAzureDevOpsSettings")]
- public List GetAzureDevOpsSettings(string rowKey = null)
+ public async Task> GetAzureDevOpsSettings(string rowKey = null)
{
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
- List settings = AzureTableStorageDA.GetAzureDevOpsSettingsFromStorage(tableStorageConfig, tableStorageConfig.TableAzureDevOpsSettings, rowKey);
+ //If the storage settings are missing, return an empty list instead of throwing an error
+ if (IsTableStorageConfigured(tableStorageConfig, tableStorageConfig.TableAzureDevOpsSettings) == false)
+ {
+ return new List();
+ }
+ List settings = await AzureTableStorageDA.GetAzureDevOpsSettingsFromStorage(tableStorageConfig, tableStorageConfig.TableAzureDevOpsSettings, rowKey);
return settings;
}
[HttpGet("GetGitHubSettings")]
- public List GetGitHubSettings(string rowKey = null)
+ public async Task> GetGitHubSettings(string rowKey = null)
{
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
- List settings = AzureTableStorageDA.GetGitHubSettingsFromStorage(tableStorageConfig, tableStorageConfig.TableGitHubSettings, rowKey);
+ //If the storage settings are missing, return an empty list instead of throwing an error
+ if (IsTableStorageConfigured(tableStorageConfig, tableStorageConfig.TableGitHubSettings) == false)
+ {
+ return new List();
+ }
+ List settings = await AzureTableStorageDA.GetGitHubSettingsFromStorage(tableStorageConfig, tableStorageConfig.TableGitHubSettings, rowKey);
return settings;
}
@@ -59,6 +69,10 @@ public async Task UpdateAzureDevOpsSetting(string patToken,
//Save everything else to table storage
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
+ if (IsTableStorageConfigured(tableStorageConfig, tableStorageConfig.TableAzureDevOpsSettings) == false)
+ {
+ return false;
+ }
return await AzureTableStorageDA.UpdateAzureDevOpsSettingInStorage(tableStorageConfig, tableStorageConfig.TableAzureDevOpsSettings,
organization, project, repository, branch, buildName, buildId, resourceGroup, itemOrder, showSetting);
}
@@ -85,6 +99,10 @@ public async Task UpdateGitHubSetting(string clientId, string clientSecret
//Save everything else to table storage
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
+ if (IsTableStorageConfigured(tableStorageConfig, tableStorageConfig.TableGitHubSettings) == false)
+ {
+ return false;
+ }
return await AzureTableStorageDA.UpdateGitHubSettingInStorage(tableStorageConfig, tableStorageConfig.TableGitHubSettings,
owner, repo, branch, workflowName, workflowId, resourceGroup, itemOrder, showSetting);
}
@@ -93,16 +111,24 @@ public async Task UpdateGitHubSetting(string clientId, string clientSecret
public async Task UpdateDevOpsMonitoringEvent([FromBody] MonitoringEvent monitoringEvent)
{
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
+ if (IsTableStorageConfigured(tableStorageConfig, tableStorageConfig.TableLog) == false)
+ {
+ return false;
+ }
return await AzureTableStorageDA.UpdateDevOpsMonitoringEventInStorage(tableStorageConfig, monitoringEvent);
}
[HttpGet("GetAzureDevOpsProjectLog")]
- public List GetAzureDevOpsProjectLog(string organization, string project, string repository)
+ public async Task> GetAzureDevOpsProjectLog(string organization, string project, string repository)
{
string partitionKey = PartitionKeys.CreateAzureDevOpsSettingsPartitionKey(organization, project, repository);
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
- return AzureTableStorageDA.GetProjectLogsFromStorage(tableStorageConfig, partitionKey);
+ if (IsTableStorageConfigured(tableStorageConfig, tableStorageConfig.TableLog) == false)
+ {
+ return new List();
+ }
+ return await AzureTableStorageDA.GetProjectLogsFromStorage(tableStorageConfig, partitionKey);
}
[HttpGet("UpdateAzureDevOpsProjectLog")]
@@ -115,16 +141,24 @@ public async Task UpdateAzureDevOpsProjectLog(string organization, string
buildsUpdated, prsUpdated, HttpUtility.UrlDecode(buildUrl), HttpUtility.UrlDecode(prUrl), exceptionMessage, exceptionStackTrace);
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
+ if (IsTableStorageConfigured(tableStorageConfig, tableStorageConfig.TableLog) == false)
+ {
+ return false;
+ }
return await AzureTableStorageDA.UpdateProjectLogInStorage(tableStorageConfig, log);
}
[HttpGet("GetGitHubProjectLog")]
- public List GetGitHubProjectLog(string owner, string repo)
+ public async Task> GetGitHubProjectLog(string owner, string repo)
{
string partitionKey = PartitionKeys.CreateGitHubSettingsPartitionKey(owner, repo);
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
- return AzureTableStorageDA.GetProjectLogsFromStorage(tableStorageConfig, partitionKey);
+ if (IsTableStorageConfigured(tableStorageConfig, tableStorageConfig.TableLog) == false)
+ {
+ return new List();
+ }
+ return await AzureTableStorageDA.GetProjectLogsFromStorage(tableStorageConfig, partitionKey);
}
[HttpGet("UpdateGitHubProjectLog")]
@@ -137,12 +171,29 @@ public async Task UpdateGitHubProjectLog(string owner, string repo,
buildsUpdated, prsUpdated, HttpUtility.UrlDecode(buildUrl), HttpUtility.UrlDecode(prUrl), exceptionMessage, exceptionStackTrace);
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
+ if (IsTableStorageConfigured(tableStorageConfig, tableStorageConfig.TableLog) == false)
+ {
+ return false;
+ }
return await AzureTableStorageDA.UpdateProjectLogInStorage(tableStorageConfig, log);
}
+ //If the table storage settings aren't available, we skip the call rather than throwing an error
+ private static bool IsTableStorageConfigured(TableStorageConfiguration tableStorageConfig, string tableName)
+ {
+ return tableStorageConfig != null &&
+ string.IsNullOrEmpty(tableStorageConfig.StorageAccountConnectionString) == false &&
+ string.IsNullOrEmpty(tableName) == false;
+ }
+
private async Task CreateKeyVaultSecret(string secretName, string secretValue)
{
string keyVaultURI = Configuration["AppSettings:KeyVaultURL"];
+ //If the key vault isn't setup, or the secret is empty, skip saving the secret rather than throwing an error
+ if (string.IsNullOrEmpty(keyVaultURI) || string.IsNullOrEmpty(secretValue))
+ {
+ return null;
+ }
SecretClient secretClient = new(new Uri(keyVaultURI), new DefaultAzureCredential());
return await secretClient.SetSecretAsync(secretName, secretValue);
}
diff --git a/src/DevOpsMetrics.Service/DevOpsMetrics.Service.csproj b/src/DevOpsMetrics.Service/DevOpsMetrics.Service.csproj
index 905c9a6b..12e25b56 100644
--- a/src/DevOpsMetrics.Service/DevOpsMetrics.Service.csproj
+++ b/src/DevOpsMetrics.Service/DevOpsMetrics.Service.csproj
@@ -1,27 +1,27 @@
-
- net7.0
- /subscriptions/07db7d0b-a6cb-4e58-b07e-e1d541c39f5b/resourcegroups/DevOpsMetrics/providers/microsoft.insights/components/devops-prod-eu-appinsights
- 4d9063eb-b4d7-4507-badc-6b916c1b050b
-
+
+ net10.0
+ /subscriptions/07db7d0b-a6cb-4e58-b07e-e1d541c39f5b/resourcegroups/DevOpsMetrics/providers/microsoft.insights/components/devops-prod-eu-appinsights
+ 4d9063eb-b4d7-4507-badc-6b916c1b050b
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
diff --git a/src/DevOpsMetrics.Service/Program.cs b/src/DevOpsMetrics.Service/Program.cs
index 0d1d9ec4..424f112a 100644
--- a/src/DevOpsMetrics.Service/Program.cs
+++ b/src/DevOpsMetrics.Service/Program.cs
@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Hosting;
-using Microsoft.Azure.KeyVault;
-using Microsoft.Azure.Services.AppAuthentication;
+using Azure.Core;
+using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
@@ -29,15 +29,27 @@ public static IHostBuilder CreateHostBuilder(string[] args)
}
//Load a connection to our Azure key vault instance
- string keyVaultURL = configuration["AppSettings:KeyVaultURL"];
+ string azureKeyVaultURL = configuration["AppSettings:KeyVaultURL"];
string clientId = configuration["AppSettings:KeyVaultClientId"];
string clientSecret = configuration["AppSettings:KeyVaultClientSecret"];
- AzureServiceTokenProvider azureServiceTokenProvider = new();
- KeyVaultClient keyVaultClient = new(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
+ string tenantId = configuration["AppSettings:TenantId"];
+ //AzureServiceTokenProvider azureServiceTokenProvider = new();
+ //KeyVaultClient keyVaultClient = new(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
//builder.AddAzureKeyVault(keyVaultURL, keyVaultClient, new DefaultKeyVaultSecretManager());
- builder.AddAzureKeyVault(keyVaultURL, clientId, clientSecret);
+ //builder.AddAzureKeyVault(keyVaultURL, clientId, clientSecret);
//configuration = builder.Build();
+ if (azureKeyVaultURL != null && clientId != null && clientSecret != null && tenantId != null)
+ {
+ TokenCredential tokenCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
+ builder.AddAzureKeyVault(new(azureKeyVaultURL), tokenCredential);
+ }
+ else
+ {
+ throw new System.Exception("Missing configuration for Azure Key Vault");
+ }
+
+
})
.ConfigureWebHostDefaults(webBuilder =>
{
diff --git a/src/DevOpsMetrics.Service/appsettings.json b/src/DevOpsMetrics.Service/appsettings.json
index db457ad3..761e2a7d 100644
--- a/src/DevOpsMetrics.Service/appsettings.json
+++ b/src/DevOpsMetrics.Service/appsettings.json
@@ -26,9 +26,10 @@
"AzureStorageAccountContainerTableLog": "DevOpsLog",
"KeyVaultURL": "https://devops-prod-eu-vault.vault.azure.net/",
"KeyVaultClientId": "",
- "KeyVaultClientSecret": ""
+ "KeyVaultClientSecret": "",
+ "TenantId": "40244aae-8aee-4af5-b221-b480db24698b"
},
"ApplicationInsights": {
- "InstrumentationKey": "dc3fb590-3612-4f09-8c95-e9378cba8654"
+ "ConnectionString": "InstrumentationKey=dc3fb590-3612-4f09-8c95-e9378cba8654;IngestionEndpoint=https://eastus-3.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus.livediagnostics.monitor.azure.com/;ApplicationId=54a63930-44fb-4776-b760-93eceafdfe01"
}
}
\ No newline at end of file
diff --git a/src/DevOpsMetrics.Tests/BaseConfiguration.cs b/src/DevOpsMetrics.Tests/BaseConfiguration.cs
index 0e60c8ec..c62be23f 100644
--- a/src/DevOpsMetrics.Tests/BaseConfiguration.cs
+++ b/src/DevOpsMetrics.Tests/BaseConfiguration.cs
@@ -1,4 +1,6 @@
using System;
+using Azure.Core;
+using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -20,12 +22,22 @@ public void TestStartUp()
string keyVaultURL = Configuration["AppSettings:KeyVaultURL"];
string clientId = Configuration["AppSettings:KeyVaultClientId"];
string clientSecret = Configuration["AppSettings:KeyVaultClientSecret"];
+ string tenantId = Configuration["AppSettings:TenantId"];
//AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
//KeyVaultClient keyVaultClient = new KeyVaultClient(
// new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
//config.AddAzureKeyVault(azureKeyVaultURL, keyVaultClient, new DefaultKeyVaultSecretManager());
- configBuilder.AddAzureKeyVault(keyVaultURL, clientId, clientSecret);
- Configuration = configBuilder.Build();
+ //configBuilder.AddAzureKeyVault(keyVaultURL, clientId, clientSecret);
+ if (keyVaultURL != null && clientId != null && clientSecret != null && tenantId != null)
+ {
+ TokenCredential tokenCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
+ configBuilder.AddAzureKeyVault(new(keyVaultURL), tokenCredential);
+ Configuration = configBuilder.Build();
+ }
+ else
+ {
+ throw new System.Exception("Missing configuration for Azure Key Vault");
+ }
}
}
diff --git a/src/DevOpsMetrics.Tests/Core/AzureMonitorTests.cs b/src/DevOpsMetrics.Tests/Core/AzureMonitorTests.cs
index ab04450c..0bc241c9 100644
--- a/src/DevOpsMetrics.Tests/Core/AzureMonitorTests.cs
+++ b/src/DevOpsMetrics.Tests/Core/AzureMonitorTests.cs
@@ -7,7 +7,7 @@
namespace DevOpsMetrics.Tests.Core
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L1Test")]
+ [TestCategory("IntegrationTest")]
[TestClass]
public class AzureMonitorTests : BaseConfiguration
{
@@ -25,7 +25,7 @@ public async Task AzureMonitorProcessingTest()
""status"": ""Activated"",
""context"": {
""timestamp"": ""2020-05-25T00:11:28.7763615Z"",
- ""id"": ""/subscriptions/07db7d0b-a6cb-4e58-b07e-e1d541c39f5b/resourceGroups/SamLearnsAzureFeatureFlags/providers/microsoft.insights/metricAlerts/ServerErrors%20featureflags-data-eu-service"",
+ ""id"": ""/subscriptions/07db7d0b-a6cb-4e58-b07e-e1d541c39f5b/resourceGroups/SamsFeatureFlags/providers/microsoft.insights/metricAlerts/ServerErrors%20featureflags-data-eu-service"",
""name"": ""ServerErrors featureflags-data-eu-service"",
""description"": """",
""conditionType"": ""SingleResourceMultipleMetricCriteria"",
@@ -51,11 +51,11 @@ public async Task AzureMonitorProcessingTest()
]
},
""subscriptionId"": ""07db7d0b-a6cb-4e58-b07e-e1d541c39f5b"",
- ""resourceGroupName"": ""SamLearnsAzureTest"",
+ ""resourceGroupName"": ""SamsFeatureFlags"",
""resourceName"": ""featureflags-data-eu-service"",
""resourceType"": ""Microsoft.Web/sites"",
- ""resourceId"": ""/subscriptions/07db7d0b-a6cb-4e58-b07e-e1d541c39f5b/resourceGroups/SamLearnsAzureFeatureFlags/providers/Microsoft.Web/sites/featureflags-data-eu-service"",
- ""portalLink"": ""https://portal.azure.com/#resource/subscriptions/07db7d0b-a6cb-4e58-b07e-e1d541c39f5b/resourceGroups/SamLearnsAzureFeatureFlags/providers/Microsoft.Web/sites/featureflags-data-eu-service""
+ ""resourceId"": ""/subscriptions/07db7d0b-a6cb-4e58-b07e-e1d541c39f5b/resourceGroups/SamsFeatureFlags/providers/Microsoft.Web/sites/featureflags-data-eu-service"",
+ ""portalLink"": ""https://portal.azure.com/#resource/subscriptions/07db7d0b-a6cb-4e58-b07e-e1d541c39f5b/resourceGroups/SamsFeatureFlags/providers/Microsoft.Web/sites/featureflags-data-eu-service""
}
}
}
@@ -66,7 +66,7 @@ public async Task AzureMonitorProcessingTest()
bool result = await da.UpdateDevOpsMonitoringEventInStorage(tableStorageConfig, monitoringEvent);
//Assert
- Assert.IsTrue(result == true);
+ Assert.IsTrue(result);
}
}
}
diff --git a/src/DevOpsMetrics.Tests/Core/ChangeFailureRateDATests.cs b/src/DevOpsMetrics.Tests/Core/ChangeFailureRateDATests.cs
index 258ab638..17b416c4 100644
--- a/src/DevOpsMetrics.Tests/Core/ChangeFailureRateDATests.cs
+++ b/src/DevOpsMetrics.Tests/Core/ChangeFailureRateDATests.cs
@@ -7,27 +7,28 @@
namespace DevOpsMetrics.Tests.Core
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L1Test")]
+ [TestCategory("IntegrationTest")]
[TestClass]
public class ChangeFailureRateDATests : BaseConfiguration
{
+ [TestCategory("UnitTest")]
[TestMethod]
- public void AzChangeFailureRateDAIntegrationTest()
+ public async Task AzChangeFailureRateDAIntegrationTest()
{
//Arrange
bool getSampleData = true;
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
string organization = "samsmithnz";
- string project = "SamLearnsAzure";
- string branch = "refs/heads/master";
- string buildName = "SamLearnsAzure.CI";
+ string project = "AzDoDevOpsMetrics";
+ string branch = "refs/heads/main";
+ string buildName = "azure-pipelines.yml";
DevOpsPlatform targetDevOpsPlatform = DevOpsPlatform.AzureDevOps;
int numberOfDays = 30;
int maxNumberOfItems = 20;
//Act
- ChangeFailureRateModel model = ChangeFailureRateDA.GetChangeFailureRate(getSampleData, tableStorageConfig,
+ ChangeFailureRateModel model = await ChangeFailureRateDA.GetChangeFailureRate(getSampleData, tableStorageConfig,
targetDevOpsPlatform, organization, project, branch, buildName, numberOfDays, maxNumberOfItems);
//Assert
@@ -74,22 +75,23 @@ public void AzChangeFailureRateDAIntegrationTest()
// Assert.AreNotEqual("Elite", model.ChangeFailureRateMetricDescription);
//}
+ [TestCategory("UnitTest")]
[TestMethod]
- public void GHChangeFailureRateDAIntegrationTest()
+ public async Task GHChangeFailureRateDAIntegrationTest()
{
//Arrange
bool getSampleData = true;
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
- string owner = "samsmithnz";
+ string owner = "DeveloperMetrics";
string repo = "DevOpsMetrics";
string branch = "main";
- string workflowName = "DevOpsMetrics CI/CD";
+ string workflowName = "CI/CD";
DevOpsPlatform targetDevOpsPlatform = DevOpsPlatform.GitHub;
int numberOfDays = 30;
int maxNumberOfItems = 20;
//Act
- ChangeFailureRateModel model = ChangeFailureRateDA.GetChangeFailureRate(getSampleData, tableStorageConfig,
+ ChangeFailureRateModel model = await ChangeFailureRateDA.GetChangeFailureRate(getSampleData, tableStorageConfig,
targetDevOpsPlatform, owner, repo, branch, workflowName, numberOfDays, maxNumberOfItems);
//Assert
@@ -111,8 +113,8 @@ public async Task UpdateChangeFailureRateDAIntegrationTest()
//Arrange
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
string organization = "samsmithnz";
- string project = "SamLearnsAzure";
- string buildName = "SamLearnsAzure.CI";
+ string project = "AzDoDevOpsMetrics";
+ string buildName = "azure-pipelines.yml";
int percent = 50;
int numberOfDays = 1;
@@ -121,7 +123,7 @@ public async Task UpdateChangeFailureRateDAIntegrationTest()
organization, project, buildName, percent, numberOfDays);
//Assert
- Assert.IsTrue(result == true);
+ Assert.IsTrue(result);
}
diff --git a/src/DevOpsMetrics.Tests/Core/ChangeFailureRateTests.cs b/src/DevOpsMetrics.Tests/Core/ChangeFailureRateTests.cs
index b251aa9c..d436a32c 100644
--- a/src/DevOpsMetrics.Tests/Core/ChangeFailureRateTests.cs
+++ b/src/DevOpsMetrics.Tests/Core/ChangeFailureRateTests.cs
@@ -7,13 +7,13 @@
namespace DevOpsMetrics.Tests.Core
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L0Test")]
+ [TestCategory("UnitTest")]
[TestClass]
public class ChangeFailureRateTests
{
[TestMethod]
- public void ChangeFailureRateEliteTest()
+ public void ChangeFailureRateHigh2Test()
{
//Arrange
ChangeFailureRate metrics = new();
@@ -39,12 +39,12 @@ public void ChangeFailureRateEliteTest()
//Assert
Assert.IsTrue(model != null);
Assert.AreEqual(0f, model.ChangeFailureRateMetric);
- Assert.AreEqual("Elite", model.ChangeFailureRateMetricDescription);
+ Assert.AreEqual("High", model.ChangeFailureRateMetricDescription);
Assert.AreEqual(false, model.IsProjectView);
}
[TestMethod]
- public void ChangeFailureRateHighTest()
+ public void ChangeFailureRateMediumTest()
{
//Arrange
ChangeFailureRate metrics = new();
@@ -64,11 +64,11 @@ public void ChangeFailureRateHighTest()
//Assert
Assert.AreEqual(0.2f, result);
- Assert.AreEqual("High", rating);
+ Assert.AreEqual("Medium", rating);
}
[TestMethod]
- public void ChangeFailureRateMediumTest()
+ public void ChangeFailureRateLowTest()
{
//Arrange
ChangeFailureRate metrics = new();
@@ -88,11 +88,11 @@ public void ChangeFailureRateMediumTest()
//Assert
Assert.AreEqual(0.4f, result);
- Assert.AreEqual("Medium", rating);
+ Assert.AreEqual("Low", rating);
}
[TestMethod]
- public void ChangeFailureRateLowTest()
+ public void ChangeFailureRateLow2Test()
{
//Arrange
ChangeFailureRate metrics = new();
diff --git a/src/DevOpsMetrics.Tests/Core/DeploymentFrequencyDATests.cs b/src/DevOpsMetrics.Tests/Core/DeploymentFrequencyDATests.cs
index 5235dd3a..fd4c97e7 100644
--- a/src/DevOpsMetrics.Tests/Core/DeploymentFrequencyDATests.cs
+++ b/src/DevOpsMetrics.Tests/Core/DeploymentFrequencyDATests.cs
@@ -7,10 +7,11 @@
namespace DevOpsMetrics.Tests.Core
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L1Test")]
+ [TestCategory("IntegrationTest")]
[TestClass]
public class DeploymentFrequencyDATests : BaseConfiguration
{
+ [TestCategory("UnitTest")]
[TestMethod]
public async Task AzDeploymentFrequencyDAIntegrationTest()
{
@@ -19,9 +20,9 @@ public async Task AzDeploymentFrequencyDAIntegrationTest()
string patToken = base.Configuration["AppSettings:AzureDevOpsPatToken"];
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(base.Configuration);
string organization = "samsmithnz";
- string project = "SamLearnsAzure";
- string branch = "refs/heads/master";
- string buildName = "SamLearnsAzure.CI";
+ string project = "AzDoDevOpsMetrics";
+ string branch = "refs/heads/main";
+ string buildName = "azure-pipelines.yml";
int numberOfDays = 30;
int maxNumberOfItems = 20;
bool useCache = true;
@@ -42,6 +43,7 @@ public async Task AzDeploymentFrequencyDAIntegrationTest()
Assert.IsTrue(model.ItemOrder == 0);
}
+ [TestCategory("UnitTest")]
[TestMethod]
public async Task GHDeploymentFrequencyDAIntegrationTest()
{
@@ -50,10 +52,10 @@ public async Task GHDeploymentFrequencyDAIntegrationTest()
string clientId = base.Configuration["AppSettings:GitHubClientId"];
string clientSecret = base.Configuration["AppSettings:GitHubClientSecret"];
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(base.Configuration);
- string owner = "samsmithnz";
+ string owner = "DeveloperMetrics";
string repo = "DevOpsMetrics";
string branch = "main";
- string workflowName = "DevOpsMetrics CI/CD";
+ string workflowName = "CI/CD";
string workflowId = "1162561";
int numberOfDays = 30;
int maxNumberOfItems = 20;
diff --git a/src/DevOpsMetrics.Tests/Core/DeploymentFrequencyTests.cs b/src/DevOpsMetrics.Tests/Core/DeploymentFrequencyTests.cs
index cbdbff3a..62cf7089 100644
--- a/src/DevOpsMetrics.Tests/Core/DeploymentFrequencyTests.cs
+++ b/src/DevOpsMetrics.Tests/Core/DeploymentFrequencyTests.cs
@@ -7,7 +7,7 @@
namespace DevOpsMetrics.Tests.Core
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L0Test")]
+ [TestCategory("UnitTest")]
[TestClass]
public class DeploymentFrequencyTests
{
@@ -33,7 +33,7 @@ public void DeploymentFrequencySingleOneDayTest()
//Assert
Assert.IsTrue(model != null);
Assert.AreEqual(1f, model.DeploymentsPerDayMetric);
- Assert.AreEqual("Elite", model.DeploymentsPerDayMetricDescription);
+ Assert.AreEqual("High", model.DeploymentsPerDayMetricDescription);
Assert.AreEqual(7, model.DeploymentsToDisplayMetric);
Assert.AreEqual("times per week", model.DeploymentsToDisplayUnit);
}
@@ -95,13 +95,13 @@ public void DeploymentFrequencyFiveSevenDaysTest()
//Assert
Assert.IsTrue(model != null);
Assert.AreEqual(0.7143f, model.DeploymentsPerDayMetric);
- Assert.AreEqual("High", model.DeploymentsPerDayMetricDescription);
+ Assert.AreEqual("Medium", model.DeploymentsPerDayMetricDescription);
Assert.AreEqual(5.0000997f, model.DeploymentsToDisplayMetric);
Assert.AreEqual("times per week", model.DeploymentsToDisplayUnit);
}
[TestMethod]
- public void DeploymentFrequencyRatingEliteTest()
+ public void DeploymentFrequencyRatingHigh2Test()
{
//Arrange
float metric = 1.01f; //daily
@@ -116,7 +116,7 @@ public void DeploymentFrequencyRatingEliteTest()
//Assert
Assert.IsTrue(model != null);
Assert.AreEqual(1.01f, model.DeploymentsPerDayMetric);
- Assert.AreEqual("Elite", model.DeploymentsPerDayMetricDescription);
+ Assert.AreEqual("High", model.DeploymentsPerDayMetricDescription);
Assert.AreEqual(1.01f, model.DeploymentsToDisplayMetric);
Assert.AreEqual("per day", model.DeploymentsToDisplayUnit);
}
@@ -137,7 +137,7 @@ public void DeploymentFrequencyRatingHighTest()
//Assert
Assert.IsTrue(model != null);
Assert.AreEqual(1f / 7f, model.DeploymentsPerDayMetric);
- Assert.AreEqual("High", model.DeploymentsPerDayMetricDescription);
+ Assert.AreEqual("Medium", model.DeploymentsPerDayMetricDescription);
Assert.AreEqual(1f, model.DeploymentsToDisplayMetric);
Assert.AreEqual("times per week", model.DeploymentsToDisplayUnit);
}
@@ -158,7 +158,7 @@ public void DeploymentFrequencyRatingMediumTest()
//Assert
Assert.IsTrue(model != null);
Assert.AreEqual(0.0055555557f, model.DeploymentsPerDayMetric);
- Assert.AreEqual("Medium", model.DeploymentsPerDayMetricDescription);
+ Assert.AreEqual("Low", model.DeploymentsPerDayMetricDescription);
Assert.AreEqual(0.16666667f, model.DeploymentsToDisplayMetric);
Assert.AreEqual("times per month", model.DeploymentsToDisplayUnit);
diff --git a/src/DevOpsMetrics.Tests/Core/FractionConverterTests.cs b/src/DevOpsMetrics.Tests/Core/FractionConverterTests.cs
index bda6d09d..93d59968 100644
--- a/src/DevOpsMetrics.Tests/Core/FractionConverterTests.cs
+++ b/src/DevOpsMetrics.Tests/Core/FractionConverterTests.cs
@@ -4,7 +4,7 @@
namespace DevOpsMetrics.Tests.Core
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L0Test")]
+ [TestCategory("UnitTest")]
[TestClass]
public class FractionConverterTests
{
diff --git a/src/DevOpsMetrics.Tests/Core/LeadTimeForChangesDATests.cs b/src/DevOpsMetrics.Tests/Core/LeadTimeForChangesDATests.cs
index 836b04e2..6694a702 100644
--- a/src/DevOpsMetrics.Tests/Core/LeadTimeForChangesDATests.cs
+++ b/src/DevOpsMetrics.Tests/Core/LeadTimeForChangesDATests.cs
@@ -8,10 +8,11 @@
namespace DevOpsMetrics.Tests.Core
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L1Test")]
+ [TestCategory("IntegrationTest")]
[TestClass]
public class LeadTimeForChangesDATests : BaseConfiguration
{
+ [TestCategory("UnitTest")]
[TestMethod]
public async Task AzLeadTimeForChangesDAIntegrationTest()
{
@@ -20,17 +21,17 @@ public async Task AzLeadTimeForChangesDAIntegrationTest()
string patToken = base.Configuration["AppSettings:AzureDevOpsPatToken"];
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(base.Configuration);
string organization = "samsmithnz";
- string project = "SamLearnsAzure";
- string repository = "SamLearnsAzure";
- string mainBranch = "refs/heads/master";
- string buildName = "SamLearnsAzure.CI";
+ string project = "AzDoDevOpsMetrics";
+ string repository = "AzDoDevOpsMetrics";
+ string branch = "refs/heads/main";
+ string buildName = "azure-pipelines.yml";
int numberOfDays = 7;
int maxNumberOfItems = 20;
bool useCache = true;
//Act
LeadTimeForChangesModel model = await LeadTimeForChangesDA.GetAzureDevOpsLeadTimesForChanges(getSampleData, patToken, tableStorageConfig,
- organization, project, repository, mainBranch, buildName,
+ organization, project, repository, branch, buildName,
numberOfDays, maxNumberOfItems, useCache);
//Assert
@@ -52,12 +53,13 @@ public async Task AzLeadTimeForChangesDAIntegrationTest()
Assert.AreEqual(1f, model.AverageBuildHours);
Assert.AreEqual(12f, model.AveragePullRequestHours);
Assert.AreEqual(13f, model.LeadTimeForChangesMetric);
- Assert.AreEqual("Elite", model.LeadTimeForChangesMetricDescription);
+ Assert.AreEqual("High", model.LeadTimeForChangesMetricDescription);
Assert.AreEqual(numberOfDays, model.NumberOfDays);
Assert.IsTrue(model.MaxNumberOfItems > 0);
Assert.IsTrue(model.TotalItems > 0);
}
+ [TestCategory("UnitTest")]
[TestMethod]
public async Task GHLeadTimeForChangesDAIntegrationTest()
{
@@ -66,10 +68,10 @@ public async Task GHLeadTimeForChangesDAIntegrationTest()
string clientId = base.Configuration["AppSettings:GitHubClientId"];
string clientSecret = base.Configuration["AppSettings:GitHubClientSecret"];
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(base.Configuration);
- string owner = "samsmithnz";
+ string owner = "DeveloperMetrics";
string repo = "devopsmetrics";
string mainBranch = "main";
- string workflowName = "DevOpsMetrics.CI";
+ string workflowName = "CI/CD";
string workflowId = "1162561";
int numberOfDays = 7;
int maxNumberOfItems = 20;
@@ -99,7 +101,7 @@ public async Task GHLeadTimeForChangesDAIntegrationTest()
Assert.AreEqual(1f, model.AverageBuildHours);
Assert.AreEqual(20.33f, model.AveragePullRequestHours);
Assert.AreEqual(21.33f, model.LeadTimeForChangesMetric);
- Assert.AreEqual("Elite", model.LeadTimeForChangesMetricDescription);
+ Assert.AreEqual("High", model.LeadTimeForChangesMetricDescription);
Assert.AreEqual(numberOfDays, model.NumberOfDays);
Assert.IsTrue(model.MaxNumberOfItems > 0);
Assert.IsTrue(model.TotalItems > 0);
diff --git a/src/DevOpsMetrics.Tests/Core/LeadTimeForChangesTests.cs b/src/DevOpsMetrics.Tests/Core/LeadTimeForChangesTests.cs
index 3db94247..6cf623e2 100644
--- a/src/DevOpsMetrics.Tests/Core/LeadTimeForChangesTests.cs
+++ b/src/DevOpsMetrics.Tests/Core/LeadTimeForChangesTests.cs
@@ -7,13 +7,13 @@
namespace DevOpsMetrics.Tests.Core
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L0Test")]
+ [TestCategory("UnitTest")]
[TestClass]
public class LeadTimeForChangesTests
{
[TestMethod]
- public void LeadTimeForChangesSingleOneDayTest()
+ public void LeadTimeForChangesSingleOneDayHighTest()
{
//Arrange
LeadTimeForChanges metrics = new();
@@ -37,7 +37,7 @@ public void LeadTimeForChangesSingleOneDayTest()
//Assert
Assert.IsTrue(model != null);
Assert.AreEqual(0.75f, model.LeadTimeForChangesMetric);
- Assert.AreEqual("Elite", model.LeadTimeForChangesMetricDescription);
+ Assert.AreEqual("High", model.LeadTimeForChangesMetricDescription);
Assert.AreEqual(0.75f, model.LeadTimeForChangesMetricDisplayMetric);
Assert.AreEqual("hours", model.LeadTimeForChangesMetricDisplayUnit);
Assert.AreEqual(true, model.IsProjectView);
@@ -61,7 +61,7 @@ public void LeadTimeForChangesNullOneDayTest()
}
[TestMethod]
- public void LeadTimeForChangesFiveSevenDaysEliteTest()
+ public void LeadTimeForChangesFiveSevenDaysHighTest()
{
//Arrange
LeadTimeForChanges metrics = new();
@@ -82,11 +82,11 @@ public void LeadTimeForChangesFiveSevenDaysEliteTest()
//Assert
Assert.AreEqual(1.05, Math.Round((double)result, 4));
- Assert.AreEqual("Elite", rating);
+ Assert.AreEqual("High", rating);
}
[TestMethod]
- public void LeadTimeForChangesFiveSevenDaysHighTest()
+ public void LeadTimeForChangesFiveSevenDaysHighTest2()
{
//Arrange
LeadTimeForChanges metrics = new();
@@ -145,7 +145,7 @@ public void LeadTimeForChangesFiveSevenDaysLowTest()
//Assert
Assert.AreEqual((24 * 31), Math.Round((double)result, 4));
- Assert.AreEqual("Medium", rating);
+ Assert.AreEqual("Low", rating);
}
diff --git a/src/DevOpsMetrics.Tests/Core/MeanTimeToRestoreDATests.cs b/src/DevOpsMetrics.Tests/Core/MeanTimeToRestoreDATests.cs
index 19e07d5f..94a51d3f 100644
--- a/src/DevOpsMetrics.Tests/Core/MeanTimeToRestoreDATests.cs
+++ b/src/DevOpsMetrics.Tests/Core/MeanTimeToRestoreDATests.cs
@@ -1,4 +1,5 @@
-using DevOpsMetrics.Core.DataAccess;
+using System.Threading.Tasks;
+using DevOpsMetrics.Core.DataAccess;
using DevOpsMetrics.Core.Models.Common;
using DevOpsMetrics.Service;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -6,12 +7,13 @@
namespace DevOpsMetrics.Tests.Core
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L1Test")]
+ [TestCategory("IntegrationTest")]
[TestClass]
public class MeanTimeToRestoreDATests : BaseConfiguration
{
+ [TestCategory("UnitTest")]
[TestMethod]
- public void MeanTimeToRestoreDAIntegrationTest()
+ public async Task MeanTimeToRestoreDAIntegrationTest()
{
//Arrange
bool getSampleData = true;
@@ -22,7 +24,7 @@ public void MeanTimeToRestoreDAIntegrationTest()
int maxNumberOfItems = 20;
//Act
- MeanTimeToRestoreModel model = MeanTimeToRestoreDA.GetAzureMeanTimeToRestore(getSampleData, tableStorageConfig, targetDevOpsPlatform, resourceGroup, numberOfDays, maxNumberOfItems);
+ MeanTimeToRestoreModel model = await MeanTimeToRestoreDA.GetAzureMeanTimeToRestore(getSampleData, tableStorageConfig, targetDevOpsPlatform, resourceGroup, numberOfDays, maxNumberOfItems);
//Assert
Assert.IsTrue(model != null);
@@ -36,18 +38,18 @@ public void MeanTimeToRestoreDAIntegrationTest()
}
[TestMethod]
- public void MeanTimeToRestoreDASamLearnsAzureProdIntegrationTest()
+ public async Task MeanTimeToRestoreDADevOpsMetricsProdIntegrationTest()
{
//Arrange
bool getSampleData = false;
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(base.Configuration);
- string resourceGroup = "SamLearnsAzureProd";
+ string resourceGroup = "DevOpsMetrics";
DevOpsPlatform targetDevOpsPlatform = DevOpsPlatform.AzureDevOps;
int numberOfDays = 30;
int maxNumberOfItems = 20;
//Act
- MeanTimeToRestoreModel model = MeanTimeToRestoreDA.GetAzureMeanTimeToRestore(getSampleData, tableStorageConfig, targetDevOpsPlatform, resourceGroup, numberOfDays, maxNumberOfItems);
+ MeanTimeToRestoreModel model = await MeanTimeToRestoreDA.GetAzureMeanTimeToRestore(getSampleData, tableStorageConfig, targetDevOpsPlatform, resourceGroup, numberOfDays, maxNumberOfItems);
//Assert
Assert.IsTrue(model != null);
@@ -62,18 +64,18 @@ public void MeanTimeToRestoreDASamLearnsAzureProdIntegrationTest()
[TestMethod]
- public void TimeToRestoreServiceDAIntegrationTest()
+ public async Task TimeToRestoreServiceDAIntegrationTest()
{
//Arrange
bool getSampleData = false;
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(base.Configuration);
- string resourceGroup = "SamLearnsAzureProd";
+ string resourceGroup = "DevOpsMetrics";
DevOpsPlatform targetDevOpsPlatform = DevOpsPlatform.AzureDevOps;
int numberOfDays = 60;
int maxNumberOfItems = 20;
//Act
- MeanTimeToRestoreModel model = MeanTimeToRestoreDA.GetAzureMeanTimeToRestore(getSampleData, tableStorageConfig, targetDevOpsPlatform, resourceGroup, numberOfDays, maxNumberOfItems);
+ MeanTimeToRestoreModel model = await MeanTimeToRestoreDA.GetAzureMeanTimeToRestore(getSampleData, tableStorageConfig, targetDevOpsPlatform, resourceGroup, numberOfDays, maxNumberOfItems);
//Assert
Assert.IsTrue(model != null);
@@ -88,18 +90,18 @@ public void TimeToRestoreServiceDAIntegrationTest()
}
[TestMethod]
- public void TimeToRestoreServiceDALiveIntegrationTest()
+ public async Task TimeToRestoreServiceDALiveIntegrationTest()
{
//Arrange
bool getSampleData = false;
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(base.Configuration);
- string resourceGroup = "SamLearnsAzureProd";
+ string resourceGroup = "DevOpsMetrics";
DevOpsPlatform targetDevOpsPlatform = DevOpsPlatform.AzureDevOps;
int numberOfDays = 60;
int maxNumberOfItems = 20;
//Act
- MeanTimeToRestoreModel model = MeanTimeToRestoreDA.GetAzureMeanTimeToRestore(getSampleData, tableStorageConfig, targetDevOpsPlatform, resourceGroup, numberOfDays, maxNumberOfItems);
+ MeanTimeToRestoreModel model = await MeanTimeToRestoreDA.GetAzureMeanTimeToRestore(getSampleData, tableStorageConfig, targetDevOpsPlatform, resourceGroup, numberOfDays, maxNumberOfItems);
//Assert
Assert.IsTrue(model != null);
diff --git a/src/DevOpsMetrics.Tests/Core/MeanTimeToRestoreTests.cs b/src/DevOpsMetrics.Tests/Core/MeanTimeToRestoreTests.cs
index 99153d2d..dad82f8f 100644
--- a/src/DevOpsMetrics.Tests/Core/MeanTimeToRestoreTests.cs
+++ b/src/DevOpsMetrics.Tests/Core/MeanTimeToRestoreTests.cs
@@ -7,13 +7,13 @@
namespace DevOpsMetrics.Tests.Core
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L0Test")]
+ [TestCategory("UnitTest")]
[TestClass]
public class MeanTimeToRestoreTests
{
[TestMethod]
- public void MTTREliteTest()
+ public void MTTRHigh2Test()
{
//Arrange
MeanTimeToRestore metrics = new();
@@ -41,7 +41,7 @@ public void MTTREliteTest()
//Assert
Assert.IsTrue(model != null);
Assert.AreEqual(0.75f, model.MTTRAverageDurationInHours);
- Assert.AreEqual("Elite", model.MTTRAverageDurationDescription);
+ Assert.AreEqual("High", model.MTTRAverageDurationDescription);
Assert.AreEqual(false, model.IsProjectView);
Assert.AreEqual(1, model.ItemOrder);
Assert.AreEqual(0.9910714f, model.SLA);
diff --git a/src/DevOpsMetrics.Tests/Core/NightlyProcessTests.cs b/src/DevOpsMetrics.Tests/Core/NightlyProcessTests.cs
deleted file mode 100644
index 843d218d..00000000
--- a/src/DevOpsMetrics.Tests/Core/NightlyProcessTests.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using DevOpsMetrics.Core.DataAccess.TableStorage;
-using DevOpsMetrics.Core.Models.Common;
-using DevOpsMetrics.Core.Models.GitHub;
-using DevOpsMetrics.Function;
-using DevOpsMetrics.Service;
-using DevOpsMetrics.Service.Controllers;
-using Microsoft.Extensions.Logging;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-namespace DevOpsMetrics.Tests.Core
-{
- [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L1Test")]
- [TestClass]
- public class NightlyProcessTests : BaseConfiguration
- {
- [TestMethod]
- public async Task GitHubProcessingTest()
- {
- //Arrange
- string clientId = Configuration["AppSettings:GitHubClientId"];
- string clientSecret = Configuration["AppSettings:GitHubClientSecret"];
- ProcessingResult result = null;
- AzureTableStorageDA da = new();
- BuildsController buildsController = new(base.Configuration, da);
- PullRequestsController pullRequestsController = new(base.Configuration, da);
- SettingsController settingsController = new(base.Configuration, da);
- using var loggerFactory = LoggerFactory.Create(loggingBuilder => loggingBuilder
- .SetMinimumLevel(LogLevel.Trace)
- .AddConsole());
- ILogger log = loggerFactory.CreateLogger();
- TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
-
- //Act
- List results = da.GetGitHubSettingsFromStorage(tableStorageConfig, tableStorageConfig.TableGitHubSettings, null);
- foreach (GitHubSettings item in results)
- {
- if (item.Repo == "AzurePipelinesToGitHubActionsConverter")
- {
- result = await Processing.ProcessGitHubItem(item, clientId, clientSecret, tableStorageConfig,
- 30, 20,
- buildsController, pullRequestsController, settingsController,
- log, 0);
- }
- }
-
- //Assert
- Assert.IsNotNull(result);
- }
-
-
-
- //[TestMethod]
- //public async Task GHUpdateAzurePipelinesToGitHubActionsConverterWebSettingDAIntegrationTest()
- //{
- // //Arrange
- // TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(base.Configuration);
- // string owner = "samsmithnz";
- // string repo = "AzurePipelinesToGitHubActionsConverterWeb";
- // string branch = "main";
- // string workflowName = "Pipelines to Actions website CI/CD";
- // string workflowId = "43084";
- // string resourceGroupName = "PipelinesToActions";
- // int itemOrder = 3;
- // bool showSetting = true;
-
- // //Act
- // AzureTableStorageDA da = new();
- // bool result = await da.UpdateGitHubSettingInStorage(tableStorageConfig, tableStorageConfig.TableGitHubSettings,
- // owner, repo, branch, workflowName, workflowId, resourceGroupName, itemOrder, showSetting);
-
- // //Assert
- // Assert.IsTrue(result == true);
- //}
-
-
- //[TestMethod]
- //public async Task GHUpdateAllDevOpsMetricsIntegrationTest()
- //{
- // //Arrange
- // string clientId = Configuration["AppSettings:GitHubClientId"];
- // string clientSecret = Configuration["AppSettings:GitHubClientSecret"];
- // int numberOfDays = 30;
- // int maxNumberOfItems = 20;
- // int totalResults = 0;
- // using var loggerFactory = LoggerFactory.Create(loggingBuilder => loggingBuilder
- // .SetMinimumLevel(LogLevel.Trace)
- // .AddConsole());
- // ILogger log = loggerFactory.CreateLogger();
- // TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
-
- // //Act
- // AzureTableStorageDA azureTableStorageDA = new();
- // BuildsController buildsController = new(Configuration, azureTableStorageDA);
- // PullRequestsController pullRequestsController = new(Configuration, azureTableStorageDA);
- // SettingsController settingsController = new(Configuration, azureTableStorageDA);
- // List ghSettings = settingsController.GetGitHubSettings();
-
- // foreach (GitHubSettings item in ghSettings)
- // {
- // ProcessingResult ghResult = await Processing.ProcessGitHubItem(item,
- // clientId, clientSecret, tableStorageConfig,
- // numberOfDays, maxNumberOfItems,
- // buildsController, pullRequestsController, settingsController, log, totalResults);
- // totalResults = ghResult.TotalResults;
- // }
-
- // //Assert
- // Assert.IsTrue(totalResults >= 0);
- //}
-
- }
-}
diff --git a/src/DevOpsMetrics.Tests/Core/PostiveNegativeListDATests.cs b/src/DevOpsMetrics.Tests/Core/PostiveNegativeListTests.cs
similarity index 98%
rename from src/DevOpsMetrics.Tests/Core/PostiveNegativeListDATests.cs
rename to src/DevOpsMetrics.Tests/Core/PostiveNegativeListTests.cs
index 6ad323b9..a1dd999d 100644
--- a/src/DevOpsMetrics.Tests/Core/PostiveNegativeListDATests.cs
+++ b/src/DevOpsMetrics.Tests/Core/PostiveNegativeListTests.cs
@@ -7,9 +7,9 @@
namespace DevOpsMetrics.Tests.Core
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L0Test")]
+ [TestCategory("UnitTest")]
[TestClass]
- public class PostiveNegativeListDATests
+ public class PostiveNegativeListTests
{
[TestMethod]
diff --git a/src/DevOpsMetrics.Tests/Core/SLATests.cs b/src/DevOpsMetrics.Tests/Core/SLATests.cs
index a7ff3316..dc8bf694 100644
--- a/src/DevOpsMetrics.Tests/Core/SLATests.cs
+++ b/src/DevOpsMetrics.Tests/Core/SLATests.cs
@@ -6,7 +6,7 @@
namespace DevOpsMetrics.Tests.Core
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L0Test")]
+ [TestCategory("UnitTest")]
[TestClass]
public class SLATests
{
diff --git a/src/DevOpsMetrics.Tests/Core/SettingsDATests.cs b/src/DevOpsMetrics.Tests/Core/SettingsDATests.cs
index 2bcfcef1..34b67286 100644
--- a/src/DevOpsMetrics.Tests/Core/SettingsDATests.cs
+++ b/src/DevOpsMetrics.Tests/Core/SettingsDATests.cs
@@ -10,7 +10,7 @@
namespace DevOpsMetrics.Tests.Core
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L1Test")]
+ [TestCategory("IntegrationTest")]
[TestClass]
public class SettingsDATests : BaseConfiguration
{
@@ -26,52 +26,53 @@ public void TestAzureSettingsExistTest()
//Act
//Assert
- Assert.IsTrue(string.IsNullOrEmpty(AzureDevOpsPatToken) == false);
- Assert.IsTrue(string.IsNullOrEmpty(GitHubClientId) == false);
- Assert.IsTrue(string.IsNullOrEmpty(GitHubClientSecret) == false);
+ //Assert.IsTrue(string.IsNullOrEmpty(AzureDevOpsPatToken) == false);
+ //Assert.IsTrue(string.IsNullOrEmpty(GitHubClientId) == false);
+ //Assert.IsTrue(string.IsNullOrEmpty(GitHubClientSecret) == false);
Assert.IsTrue(string.IsNullOrEmpty(StorageAccountConnectionString) == false);
}
[TestMethod]
- public void AzGetSamLearnsAzureSettingDAIntegrationTest()
+ public async Task AzGetAzDoDevOpsMetricsSettingDAIntegrationTest()
{
//Arrange
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(base.Configuration);
//Act
AzureTableStorageDA da = new();
- List results = da.GetAzureDevOpsSettingsFromStorage(tableStorageConfig, tableStorageConfig.TableAzureDevOpsSettings, null);
+ List results = await da.GetAzureDevOpsSettingsFromStorage(tableStorageConfig, tableStorageConfig.TableAzureDevOpsSettings, null);
//Assert
Assert.IsTrue(results.Count > 0);
}
[TestMethod]
- public void GHGetSamLearnsAzureSettingDAIntegrationTest()
+ public async Task GHGetSettingDAIntegrationTest()
{
+
//Arrange
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(base.Configuration);
//Act
AzureTableStorageDA da = new();
- List results = da.GetGitHubSettingsFromStorage(tableStorageConfig, tableStorageConfig.TableGitHubSettings, null);
+ List results = await da.GetGitHubSettingsFromStorage(tableStorageConfig, tableStorageConfig.TableGitHubSettings, null);
//Assert
Assert.IsTrue(results.Count > 0);
}
[TestMethod]
- public async Task AzUpdateSamLearnsAzureSettingDAIntegrationTest()
+ public async Task AzUpdateAzDoDevOpsMetricsSettingDAIntegrationTest()
{
//Arrange
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(base.Configuration);
string organization = "samsmithnz";
- string project = "SamLearnsAzure";
- string repository = "SamLearnsAzure";
- string branch = "refs/heads/master";
- string buildName = "SamLearnsAzure.CI";
+ string project = "AzDoDevOpsMetrics";
+ string repository = "AzDoDevOpsMetrics";
+ string branch = "refs/heads/main";
+ string buildName = "azure-pipelines.yml";
string buildId = "3673";
- string resourceGroupName = "SamLearnsAzureProd";
+ string resourceGroupName = "DevOpsMetrics";
int itemOrder = 1;
bool showSetting = true;
@@ -81,7 +82,7 @@ public async Task AzUpdateSamLearnsAzureSettingDAIntegrationTest()
organization, project, repository, branch, buildName, buildId, resourceGroupName, itemOrder, showSetting);
//Assert
- Assert.IsTrue(result == true);
+ Assert.IsTrue(result);
}
[TestMethod]
@@ -89,7 +90,7 @@ public async Task GHUpdateDevOpsMetricsSettingDAIntegrationTest()
{
//Arrange
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(base.Configuration);
- string owner = "samsmithnz";
+ string owner = "DeveloperMetrics";
string repo = "DevOpsMetrics";
string branch = "main";
string workflowName = "CI/CD";
@@ -104,7 +105,7 @@ public async Task GHUpdateDevOpsMetricsSettingDAIntegrationTest()
owner, repo, branch, workflowName, workflowId, resourceGroupName, itemOrder, showSetting);
//Assert
- Assert.IsTrue(result == true);
+ Assert.IsTrue(result);
}
[TestMethod]
@@ -117,7 +118,7 @@ public async Task GHUpdateSamsFeatureFlagsSettingDAIntegrationTest()
string branch = "main";
string workflowName = "SamsFeatureFlags CI/CD";
string workflowId = "108084";
- string resourceGroupName = "SamLearnsAzureFeatureFlags";
+ string resourceGroupName = "SamsFeatureFlags";
int itemOrder = 3;
bool showSetting = true;
@@ -127,7 +128,7 @@ public async Task GHUpdateSamsFeatureFlagsSettingDAIntegrationTest()
owner, repo, branch, workflowName, workflowId, resourceGroupName, itemOrder, showSetting);
//Assert
- Assert.IsTrue(result == true);
+ Assert.IsTrue(result);
}
[TestMethod]
@@ -150,7 +151,7 @@ public async Task GHUpdateAzurePipelinesToGitHubActionsConverterWebSettingDAInte
owner, repo, branch, workflowName, workflowId, resourceGroupName, itemOrder, showSetting);
//Assert
- Assert.IsTrue(result == true);
+ Assert.IsTrue(result);
}
}
diff --git a/src/DevOpsMetrics.Tests/DevOpsMetrics.Tests.csproj b/src/DevOpsMetrics.Tests/DevOpsMetrics.Tests.csproj
index 71c29b30..fca76064 100644
--- a/src/DevOpsMetrics.Tests/DevOpsMetrics.Tests.csproj
+++ b/src/DevOpsMetrics.Tests/DevOpsMetrics.Tests.csproj
@@ -1,46 +1,48 @@
-
- net7.0
-
- false
-
- 0e2b71e7-af1c-40d5-a334-1118f60b467e
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
-
-
+
+ net10.0
+
+ false
+
+ 0e2b71e7-af1c-40d5-a334-1118f60b467e
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
diff --git a/src/DevOpsMetrics.Tests/Service/BadgeTests.cs b/src/DevOpsMetrics.Tests/Service/BadgeTests.cs
index 1d60fb4a..4e94b99b 100644
--- a/src/DevOpsMetrics.Tests/Service/BadgeTests.cs
+++ b/src/DevOpsMetrics.Tests/Service/BadgeTests.cs
@@ -4,12 +4,11 @@
namespace DevOpsMetrics.Tests.Service
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L0Test")]
+ [TestCategory("UnitTest")]
[TestClass]
public class BadgeTests
{
- [TestCategory("L0Test")]
[TestMethod]
public void DeploymentFrequencyEliteBadgeTest()
{
@@ -29,7 +28,6 @@ public void DeploymentFrequencyEliteBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void DeploymentFrequencyHighBadgeTest()
{
@@ -47,7 +45,6 @@ public void DeploymentFrequencyHighBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void DeploymentFrequencyMediumBadgeTest()
{
@@ -65,7 +62,6 @@ public void DeploymentFrequencyMediumBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void DeploymentFrequencyLowBadgeTest()
{
@@ -83,7 +79,6 @@ public void DeploymentFrequencyLowBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void DeploymentFrequencyNoneBadgeTest()
{
@@ -100,26 +95,6 @@ public void DeploymentFrequencyNoneBadgeTest()
Assert.AreEqual("https://img.shields.io/badge/Deployment%20frequency-None-lightgrey", model.BadgeURL);
}
- [TestCategory("L0Test")]
- [TestMethod]
- public void ChangeFailureRateControllerEliteBadgeTest()
- {
- //Arrange
- ChangeFailureRateModel model = new()
- {
- ChangeFailureRateMetricDescription = "Elite",
- ChangeFailureRateMetric = 0
- };
-
- //Act
-
- //Assert
- Assert.AreEqual("Elite", model.ChangeFailureRateMetricDescription);
- Assert.AreEqual("https://img.shields.io/badge/Change%20failure%20rate-Elite-brightgreen", model.BadgeURL);
- Assert.AreEqual("https://img.shields.io/badge/Change%20failure%20rate%20%280.00%25%29-Elite-brightgreen", model.BadgeWithMetricURL);
- }
-
- [TestCategory("L0Test")]
[TestMethod]
public void ChangeFailureRateControllerHighBadgeTest()
{
@@ -137,7 +112,6 @@ public void ChangeFailureRateControllerHighBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void ChangeFailureRateControllerMediumBadgeTest()
{
@@ -155,7 +129,6 @@ public void ChangeFailureRateControllerMediumBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void ChangeFailureRateControllerLowBadgeTest()
{
@@ -173,7 +146,6 @@ public void ChangeFailureRateControllerLowBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void ChangeFailureRateControllerNoneBadgeTest()
{
@@ -190,7 +162,6 @@ public void ChangeFailureRateControllerNoneBadgeTest()
Assert.AreEqual("https://img.shields.io/badge/Change%20failure%20rate-None-lightgrey", model.BadgeURL);
}
- [TestCategory("L0Test")]
[TestMethod]
public void LeadTimeForChangesEliteBadgeTest()
{
@@ -210,7 +181,6 @@ public void LeadTimeForChangesEliteBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void LeadTimeForChangesHighBadgeTest()
{
@@ -228,7 +198,6 @@ public void LeadTimeForChangesHighBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void LeadTimeForChangesMediumBadgeTest()
{
@@ -246,7 +215,6 @@ public void LeadTimeForChangesMediumBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void LeadTimeForChangesLowBadgeTest()
{
@@ -264,7 +232,6 @@ public void LeadTimeForChangesLowBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void LeadTimeForChangesNoneBadgeTest()
{
@@ -282,7 +249,6 @@ public void LeadTimeForChangesNoneBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void MeanTimeToRestoreEliteBadgeTest()
{
@@ -302,7 +268,6 @@ public void MeanTimeToRestoreEliteBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void MeanTimeToRestoreHighBadgeTest()
{
@@ -320,7 +285,6 @@ public void MeanTimeToRestoreHighBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void MeanTimeToRestoreMediumBadgeTest()
{
@@ -338,7 +302,6 @@ public void MeanTimeToRestoreMediumBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void MeanTimeToRestoreLowBadgeTest()
{
@@ -356,7 +319,6 @@ public void MeanTimeToRestoreLowBadgeTest()
}
- [TestCategory("L0Test")]
[TestMethod]
public void MeanTimeToRestoreNoneBadgeTest()
{
diff --git a/src/DevOpsMetrics.Tests/Service/ChangeFailureRateTests.cs b/src/DevOpsMetrics.Tests/Service/ChangeFailureRateControllerTests.cs
similarity index 59%
rename from src/DevOpsMetrics.Tests/Service/ChangeFailureRateTests.cs
rename to src/DevOpsMetrics.Tests/Service/ChangeFailureRateControllerTests.cs
index 6ed92ecf..9926219b 100644
--- a/src/DevOpsMetrics.Tests/Service/ChangeFailureRateTests.cs
+++ b/src/DevOpsMetrics.Tests/Service/ChangeFailureRateControllerTests.cs
@@ -1,30 +1,31 @@
-using DevOpsMetrics.Core.Models.Common;
+using System.Threading.Tasks;
+using DevOpsMetrics.Core.Models.Common;
using DevOpsMetrics.Service.Controllers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace DevOpsMetrics.Tests.Service
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L1Test")]
+ [TestCategory("UnitTest")]
[TestClass]
- public class ChangeFailureRateTests : BaseConfiguration
+ public class ChangeFailureRateControllerTests : BaseConfiguration
{
[TestMethod]
- public void AzChangeFailureRateSampleControllerIntegrationTest()
+ public async Task AzChangeFailureRateSampleControllerIntegrationTest()
{
//Arrange
bool getSampleData = true;
string organization = "samsmithnz";
- string project = "SamLearnsAzure";
- string branch = "refs/heads/master";
- string buildName = "SamLearnsAzure.CI";
+ string project = "AzDoDevOpsMetrics";
+ string branch = "refs/heads/main";
+ string buildName = "azure-pipelines.yml";
DevOpsPlatform targetDevOpsPlatform = DevOpsPlatform.AzureDevOps;
int numberOfDays = 7;
int maxNumberOfItems = 20;
ChangeFailureRateController controller = new(base.Configuration);
//Act
- ChangeFailureRateModel model = controller.GetChangeFailureRate(getSampleData,
+ ChangeFailureRateModel model = await controller.GetChangeFailureRate(getSampleData,
targetDevOpsPlatform, organization, project, branch, buildName, numberOfDays, maxNumberOfItems);
//Assert
@@ -39,16 +40,16 @@ public void AzChangeFailureRateSampleControllerIntegrationTest()
Assert.IsTrue(model.TotalItems > 0);
}
- //[TestCategory("L1Test")]
+ //[TestCategory("IntegrationTest")]
//[TestMethod]
//public void AzChangeFailureRateLiveControllerIntegrationTest()
//{
// //Arrange
// bool getSampleData = false;
// string organization = "samsmithnz";
- // string project = "SamLearnsAzure";
- // string branch = "refs/heads/master";
- // string buildName = "SamLearnsAzure.CI";
+ //string project = "AzDoDevOpsMetrics";
+ //string branch = "refs/heads/main";
+ //string buildName = "azure-pipelines.yml";
// DevOpsPlatform targetDevOpsPlatform = DevOpsPlatform.AzureDevOps;
// int numberOfDays = 30;
// int maxNumberOfItems = 20;
@@ -69,23 +70,26 @@ public void AzChangeFailureRateSampleControllerIntegrationTest()
// Assert.IsTrue(model.TotalItems > 0);
//}
- [TestCategory("L1Test")]
[TestMethod]
- public void GHChangeFailureRateSampleControllerIntegrationTest()
+ public async Task GHChangeFailureRateSampleControllerIntegrationTest()
{
//Arrange
bool getSampleData = true;
+ //string owner = "samsmithnz";
+ //string repo = "SamsFeatureFlags";
+ //string branch = "main";
+ //string workflowName = "SamsFeatureFlags.CI/CD";
string owner = "samsmithnz";
- string repo = "SamsFeatureFlags";
+ string repo = "AzurePipelinesToGitHubActionsConverter";
string branch = "main";
- string workflowName = "SamsFeatureFlags.CI/CD";
+ string workflowName = "CI/ CD";
DevOpsPlatform targetDevOpsPlatform = DevOpsPlatform.GitHub;
int numberOfDays = 7;
int maxNumberOfItems = 20;
ChangeFailureRateController controller = new(base.Configuration);
//Act
- ChangeFailureRateModel model = controller.GetChangeFailureRate(getSampleData,
+ ChangeFailureRateModel model = await controller.GetChangeFailureRate(getSampleData,
targetDevOpsPlatform, owner, repo, branch, workflowName, numberOfDays, maxNumberOfItems);
//Assert
@@ -100,36 +104,36 @@ public void GHChangeFailureRateSampleControllerIntegrationTest()
Assert.IsTrue(model.TotalItems > 0);
}
- //[TestCategory("L1Test")]
- //[TestMethod]
- //public void GHChangeFailureRateLiveControllerIntegrationTest()
- //{
- // //Arrange
- // bool getSampleData = false;
- // string owner = "samsmithnz";
- // string repo = "SamsFeatureFlags";
- // string branch = "main";
- // string workflowName = "SamsFeatureFlags.CI/CD";
- // DevOpsPlatform targetDevOpsPlatform = DevOpsPlatform.GitHub;
- // int numberOfDays = 7;
- // int maxNumberOfItems = 20;
- // ChangeFailureRateController controller = new(base.Configuration);
+ [TestCategory("IntegrationTest")]
+ [TestMethod]
+ public async Task GHChangeFailureRateLiveControllerIntegrationTest()
+ {
+ //Arrange
+ bool getSampleData = false;
+ string owner = "samsmithnz";
+ string repo = "AzurePipelinesToGitHubActionsConverter";
+ string branch = "main";
+ string workflowName = "CI/ CD";
+ DevOpsPlatform targetDevOpsPlatform = DevOpsPlatform.GitHub;
+ int numberOfDays = 30;
+ int maxNumberOfItems = 20;
+ ChangeFailureRateController controller = new(base.Configuration);
- // //Act
- // ChangeFailureRateModel model = controller.GetChangeFailureRate(getSampleData,
- // targetDevOpsPlatform, owner, repo, branch, workflowName, numberOfDays, maxNumberOfItems);
+ //Act
+ ChangeFailureRateModel model = await controller.GetChangeFailureRate(getSampleData,
+ targetDevOpsPlatform, owner, repo, branch, workflowName, numberOfDays, maxNumberOfItems);
- // //Assert
- // Assert.IsTrue(model != null);
- // Assert.IsTrue(model.TargetDevOpsPlatform == targetDevOpsPlatform);
- // Assert.IsTrue(model.DeploymentName == workflowName);
- // Assert.IsTrue(model.ChangeFailureRateMetric >= 0f);
- // Assert.AreEqual(false, string.IsNullOrEmpty(model.ChangeFailureRateMetricDescription));
- // Assert.AreEqual(numberOfDays, model.NumberOfDays);
- // Assert.IsTrue(model.MaxNumberOfItems > 0);
- // Assert.IsTrue(model.TotalItems > 0);
+ //Assert
+ Assert.IsTrue(model != null);
+ Assert.IsTrue(model.TargetDevOpsPlatform == targetDevOpsPlatform);
+ Assert.IsTrue(model.DeploymentName == workflowName);
+ Assert.IsTrue(model.ChangeFailureRateMetric >= -1f);
+ Assert.AreEqual(false, string.IsNullOrEmpty(model.ChangeFailureRateMetricDescription));
+ Assert.AreEqual(numberOfDays, model.NumberOfDays);
+ Assert.IsTrue(model.MaxNumberOfItems >= 0);
+ Assert.IsTrue(model.TotalItems >= 0);
- //}
+ }
}
diff --git a/src/DevOpsMetrics.Tests/Service/DORASummaryControllerTests.cs b/src/DevOpsMetrics.Tests/Service/DORASummaryControllerTests.cs
index e964deab..513012c2 100644
--- a/src/DevOpsMetrics.Tests/Service/DORASummaryControllerTests.cs
+++ b/src/DevOpsMetrics.Tests/Service/DORASummaryControllerTests.cs
@@ -1,40 +1,218 @@
-using DevOpsMetrics.Core.Models.Common;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Threading.Tasks;
+using DevOpsMetrics.Core.DataAccess.TableStorage;
+using DevOpsMetrics.Core.Models.AzureDevOps;
+using DevOpsMetrics.Core.Models.Common;
+using DevOpsMetrics.Core.Models.GitHub;
using DevOpsMetrics.Service.Controllers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace DevOpsMetrics.Tests.Service
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L1Test")]
+ [TestCategory("IntegrationTest")]
[TestClass]
public class DORASummaryControllerTests : BaseConfiguration
{
[TestMethod]
- public void DORASummaryControllerGetIntegrationTest()
+ public async Task DORASummaryControllerGetIntegrationTest()
{
//Arrange
+ string organization = "DeveloperMetrics";
+ string project = null;
+ string repo = "DevOpsMetrics";
+ DORASummaryController controller = new(base.Configuration);
+
+ //Act
+ DORASummaryItem model = await controller.GetDORASummaryItem(organization, project, repo);
+
+ //Assert
+ Assert.IsNotNull(model);
+
+ }
+
+ [TestMethod]
+ public async Task DORASummaryControllerGitHubUpdateIntegrationTest()
+ {
+ //Arrange
+ string project = null;
+ //string organization = "DeveloperMetrics";
+ //string repo = "DevOpsMetrics";
string organization = "samsmithnz";
- string repository = "AzurePipelinesToGitHubActionsConverter";
+ string repo = "SamsFeatureFlags";
+ //string organization = "samsmithnz";
+ //string repo = "CustomQueue";
+ //string organization = "samsmithnz";
+ //string project = "SamLearnsAzure";
+ //string repo = "SamLearnsAzure";
+ int numberOfDays = 30;
+ int maxNumberOfItems = 20;
DORASummaryController controller = new(base.Configuration);
+ ProcessingResult model;
+ DORASummaryItem doraSummaryItem;
//Act
- DORASummaryItem model = controller.GetDORASummaryItems(organization, repository);
+ (AzureDevOpsSettings, GitHubSettings) setting = await GetSettingWithName(organization, project, repo);
+ if (setting.Item1 != null)
+ {
+ AzureDevOpsSettings azureDevOpsSettings = setting.Item1;
+ model = await controller.UpdateDORASummaryItem(azureDevOpsSettings.Organization,
+ azureDevOpsSettings.Project, azureDevOpsSettings.Repository,
+ azureDevOpsSettings.Branch,
+ azureDevOpsSettings.BuildName, azureDevOpsSettings.BuildId,
+ azureDevOpsSettings.ProductionResourceGroup,
+ numberOfDays, maxNumberOfItems,
+ null, true, false);
+ doraSummaryItem = await controller.GetDORASummaryItem(azureDevOpsSettings.Organization, azureDevOpsSettings.Project, azureDevOpsSettings.Repository);
+ }
+ else if (setting.Item2 != null)
+ {
+ GitHubSettings gitHubSettings = setting.Item2;
+ model = await controller.UpdateDORASummaryItem(gitHubSettings.Owner, "", gitHubSettings.Repo,
+ gitHubSettings.Branch, gitHubSettings.WorkflowName, gitHubSettings.WorkflowId, gitHubSettings.ProductionResourceGroup,
+ numberOfDays, maxNumberOfItems,
+ null, true, true);
+ doraSummaryItem = await controller.GetDORASummaryItem(gitHubSettings.Owner, null, gitHubSettings.Repo);
+ }
+ else
+ {
+ model = await controller.UpdateDORASummaryItem(organization, "", repo,
+ "main", "", "", "",
+ numberOfDays, maxNumberOfItems,
+ null, true, true);
+ doraSummaryItem = await controller.GetDORASummaryItem(organization, project, repo);
+ }
+
//Assert
Assert.IsNotNull(model);
- //Assert.AreEqual(DevOpsPlatform.AzureDevOps, model.TargetDevOpsPlatform);
- //Assert.AreEqual(buildName, model.DeploymentName);
- //Assert.AreEqual(10f, model.DeploymentsPerDayMetric);
- //Assert.AreEqual("Elite", model.DeploymentsPerDayMetricDescription);
- //Assert.AreEqual(10, model.BuildList.Count);
- //Assert.AreEqual(70, model.BuildList[0].BuildDurationPercent);
- //Assert.AreEqual("1", model.BuildList[0].BuildNumber);
- //Assert.AreEqual("main", model.BuildList[0].Branch);
- //Assert.AreEqual("completed", model.BuildList[0].Status);
- //Assert.AreEqual("https://dev.azure.com/samsmithnz/samlearnsazure/1", model.BuildList[0].Url);
- //Assert.IsTrue(model.BuildList[0].StartTime > DateTime.MinValue);
- //Assert.IsTrue(model.BuildList[0].EndTime > DateTime.MinValue);
+ Assert.IsNotNull(doraSummaryItem);
+ Assert.IsTrue(doraSummaryItem.DeploymentFrequency >= 0);
+ Assert.IsTrue(doraSummaryItem.LeadTimeForChanges >= 0);
+ Assert.IsTrue(doraSummaryItem.MeanTimeToRestore >= 0);
+ Assert.IsTrue(doraSummaryItem.ChangeFailureRate >= -1); //Change failure rate is -1 when there is no data (since 0 means something different from this metric)
+ Assert.IsTrue(doraSummaryItem.LastUpdatedMessage.IndexOf("Error") == -1);
+ Assert.IsTrue(doraSummaryItem.LastUpdated > DateTime.MinValue);
+ }
+
+ [TestMethod]
+ public async Task DORASummaryControllerAzureDevOpsUpdateIntegrationTest()
+ {
+ //Arrange
+ string organization = "samsmithnz";
+ string project = "AzDoDevOpsMetrics";
+ string repository = "AzDoDevOpsMetrics";
+ int numberOfDays = 30;
+ int maxNumberOfItems = 20;
+ DORASummaryController controller = new(base.Configuration);
+ ProcessingResult model;
+
+ //Act
+ (AzureDevOpsSettings, GitHubSettings) setting = await GetSettingWithName(organization, project, repository);
+ if (setting.Item1 != null)
+ {
+ AzureDevOpsSettings azureDevOpsSettings = setting.Item1;
+ model = await controller.UpdateDORASummaryItem(azureDevOpsSettings.Organization,
+ azureDevOpsSettings.Project, azureDevOpsSettings.Repository,
+ azureDevOpsSettings.Branch,
+ azureDevOpsSettings.BuildName, azureDevOpsSettings.BuildId,
+ azureDevOpsSettings.ProductionResourceGroup,
+ numberOfDays, maxNumberOfItems,
+ null, true, false);
+ }
+ else
+ {
+ GitHubSettings gitHubSettings = setting.Item2;
+ model = await controller.UpdateDORASummaryItem(gitHubSettings.Owner, "", gitHubSettings.Repo,
+ gitHubSettings.Branch, gitHubSettings.WorkflowName, gitHubSettings.WorkflowId, gitHubSettings.ProductionResourceGroup,
+ numberOfDays, maxNumberOfItems,
+ null, true, true);
+ }
+
+ //Assert
+ Assert.IsNotNull(model);
+ }
+
+ [TestMethod]
+ public async Task DORASummaryControllerUpdateALLIntegrationTest()
+ {
+ //Arrange
+ int numberOfDays = 30;
+ int maxNumberOfItems = 20;
+ int totalResults = 0;
+ DORASummaryController controller = new(base.Configuration);
+ SettingsController settingsController = new(base.Configuration, new AzureTableStorageDA());
+ bool runExpensiveTest = false;
+
+ //Act
+ if (runExpensiveTest)
+ {
+ List azSettings = await settingsController.GetAzureDevOpsSettings();
+ List ghSettings = await settingsController.GetGitHubSettings();
+
+ foreach (AzureDevOpsSettings azSetting in azSettings)
+ {
+ Debug.WriteLine($"Processing Azure DevOps organization {azSetting.Organization}, project {azSetting.Project}");
+ ProcessingResult ghResult = await controller.UpdateDORASummaryItem(
+ azSetting.Organization, azSetting.Project, azSetting.Repository,
+ azSetting.Branch, azSetting.BuildName, azSetting.BuildId,
+ azSetting.ProductionResourceGroup,
+ numberOfDays, maxNumberOfItems,
+ null, true, false);
+ totalResults += ghResult.TotalResults;
+ }
+
+ foreach (GitHubSettings ghSetting in ghSettings)
+ {
+ Debug.WriteLine($"Processing GitHub owner {ghSetting.Owner}, repo {ghSetting.Repo}");
+ ProcessingResult ghResult = await controller.UpdateDORASummaryItem(
+ ghSetting.Owner, "", ghSetting.Repo, ghSetting.Branch,
+ ghSetting.WorkflowName, ghSetting.WorkflowId,
+ ghSetting.ProductionResourceGroup,
+ numberOfDays, maxNumberOfItems,
+ null, true, true);
+ totalResults += ghResult.TotalResults;
+ }
+ }
+
+ //Assert
+ if (runExpensiveTest)
+ {
+ Assert.IsTrue(totalResults > 0);
+ }
+ else
+ {
+ Assert.IsTrue(totalResults == 0);
+ }
+ }
+
+ private async Task<(AzureDevOpsSettings, GitHubSettings)> GetSettingWithName(string owner, string project, string repo)
+ {
+ (AzureDevOpsSettings, GitHubSettings) results = (null, null);
+ SettingsController settingsController = new(base.Configuration, new AzureTableStorageDA());
+ List azSettings = await settingsController.GetAzureDevOpsSettings();
+ List ghSettings = await settingsController.GetGitHubSettings();
+
+ foreach (AzureDevOpsSettings azSetting in azSettings)
+ {
+ if (azSetting.Organization == owner && azSetting.Project == project && azSetting.Repository == repo)
+ {
+ results = (azSetting, null);
+ break;
+ }
+ }
+ foreach (GitHubSettings ghSetting in ghSettings)
+ {
+ if (ghSetting.Owner == owner && ghSetting.Repo == repo)
+ {
+ results = (null, ghSetting);
+ break;
+ }
+ }
+ return results;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/DevOpsMetrics.Tests/Service/DeploymentFrequencyControllerTests.cs b/src/DevOpsMetrics.Tests/Service/DeploymentFrequencyControllerTests.cs
index f3572d71..a119fa67 100644
--- a/src/DevOpsMetrics.Tests/Service/DeploymentFrequencyControllerTests.cs
+++ b/src/DevOpsMetrics.Tests/Service/DeploymentFrequencyControllerTests.cs
@@ -7,20 +7,21 @@
namespace DevOpsMetrics.Tests.Service
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L1Test")]
+ [TestCategory("IntegrationTest")]
[TestClass]
- public class DeploymentFrequencyL1s : BaseConfiguration
+ public class DeploymentFrequencyControllerTests : BaseConfiguration
{
+ [TestCategory("UnitTest")]
[TestMethod]
public async Task AzDeploymentsSampleControllerIntegrationTest()
{
//Arrange
bool getSampleData = true;
string organization = "samsmithnz";
- string project = "SamLearnsAzure";
- string repository = "SamLearnsAzure";
- string branch = "refs/heads/master";
- string buildName = "SamLearnsAzure.CI";
+ string project = "AzDoDevOpsMetrics";
+ string repository = "AzDoDevOpsMetrics";
+ string branch = "refs/heads/main";
+ string buildName = "azure-pipelines.yml";
int numberOfDays = 7;
int maxNumberOfItems = 20;
bool useCache = false;
@@ -33,7 +34,7 @@ public async Task AzDeploymentsSampleControllerIntegrationTest()
Assert.AreEqual(DevOpsPlatform.AzureDevOps, model.TargetDevOpsPlatform);
Assert.AreEqual(buildName, model.DeploymentName);
Assert.AreEqual(10f, model.DeploymentsPerDayMetric);
- Assert.AreEqual("Elite", model.DeploymentsPerDayMetricDescription);
+ Assert.AreEqual("High", model.DeploymentsPerDayMetricDescription);
Assert.AreEqual(10, model.BuildList.Count);
Assert.AreEqual(70, model.BuildList[0].BuildDurationPercent);
Assert.AreEqual("1", model.BuildList[0].BuildNumber);
@@ -51,10 +52,10 @@ public async Task AzDeploymentsAPIControllerIntegrationTest()
//Arrange
bool getSampleData = false;
string organization = "samsmithnz";
- string project = "SamLearnsAzure";
- string repository = "SamLearnsAzure";
- string branch = "refs/heads/master";
- string buildName = "SamLearnsAzure.CI";
+ string project = "AzDoDevOpsMetrics";
+ string repository = "AzDoDevOpsMetrics";
+ string branch = "refs/heads/main";
+ string buildName = "azure-pipelines.yml";
int numberOfDays = 30;
int maxNumberOfItems = 20;
bool useCache = false;
@@ -92,15 +93,15 @@ public async Task AzDeploymentsAPIControllerIntegrationTest()
[TestMethod]
public async Task AzDeploymentsCacheControllerIntegrationTest()
{
- //https://devops-prod-eu-service.azurewebsites.net//api/DeploymentFrequency/GetAzureDevOpsDeploymentFrequency?getSampleData=False&organization=samsmithnz&project=SamLearnsAzure&repository=SamLearnsAzure&branch=refs/heads/master&buildName=SamLearnsAzure.CI&buildId=3673&numberOfDays=30&maxNumberOfItems=20&useCache=true
+ //https://devops-prod-eu-service.azurewebsites.net//api/DeploymentFrequency/GetAzureDevOpsDeploymentFrequency?getSampleData=False&organization=samsmithnz&project=AzDoDevOpsMetrics&repository=SamLearnsAzure&branch=refs/heads/main&buildName=azure-pipelines.yml&buildId=3673&numberOfDays=30&maxNumberOfItems=20&useCache=true
//Arrange
bool getSampleData = false;
string organization = "samsmithnz";
- string project = "SamLearnsAzure";
- string repository = "SamLearnsAzure";
- string branch = "refs/heads/master";
- string buildName = "SamLearnsAzure.CI";
+ string project = "AzDoDevOpsMetrics";
+ string repository = "AzDoDevOpsMetrics";
+ string branch = "refs/heads/main";
+ string buildName = "azure-pipelines.yml";
int numberOfDays = 30;
int maxNumberOfItems = 20;
bool useCache = true;
@@ -135,6 +136,7 @@ public async Task AzDeploymentsCacheControllerIntegrationTest()
}
+ [TestCategory("UnitTest")]
[TestMethod]
public async Task GHDeploymentsSampleControllerIntegrationTest()
{
@@ -157,7 +159,7 @@ public async Task GHDeploymentsSampleControllerIntegrationTest()
Assert.AreEqual(DevOpsPlatform.GitHub, model.TargetDevOpsPlatform);
Assert.AreEqual(workflowName, model.DeploymentName);
Assert.AreEqual(10f, model.DeploymentsPerDayMetric);
- Assert.AreEqual("Elite", model.DeploymentsPerDayMetricDescription);
+ Assert.AreEqual("High", model.DeploymentsPerDayMetricDescription);
Assert.AreEqual(10, model.BuildList.Count);
Assert.AreEqual(70, model.BuildList[0].BuildDurationPercent);
Assert.AreEqual("1", model.BuildList[0].BuildNumber);
@@ -267,10 +269,10 @@ public async Task AzDeploymentsControllerAPILiveWithCacheIntegrationTest()
//Arrange
bool getSampleData = false;
string organization = "samsmithnz";
- string project = "SamLearnsAzure";
- string repository = "SamLearnsAzure";
- string branch = "refs/heads/master";
- string buildName = "SamLearnsAzure.CI";
+ string project = "AzDoDevOpsMetrics";
+ string repository = "AzDoDevOpsMetrics";
+ string branch = "refs/heads/main";
+ string buildName = "azure-pipelines.yml";
int numberOfDays = 7;
int maxNumberOfItems = 20;
bool useCache = true;
@@ -306,6 +308,7 @@ public async Task AzDeploymentsControllerAPILiveWithCacheIntegrationTest()
+ [TestCategory("UnitTest")]
[TestMethod]
public async Task GHDeploymentsControllerAPILiveWithCacheIntegrationTest()
{
@@ -328,7 +331,7 @@ public async Task GHDeploymentsControllerAPILiveWithCacheIntegrationTest()
Assert.AreEqual(DevOpsPlatform.GitHub, model.TargetDevOpsPlatform);
Assert.AreEqual(workflowName, model.DeploymentName);
Assert.AreEqual(10f, model.DeploymentsPerDayMetric);
- Assert.AreEqual("Elite", model.DeploymentsPerDayMetricDescription);
+ Assert.AreEqual("High", model.DeploymentsPerDayMetricDescription);
Assert.AreEqual(10, model.BuildList.Count);
Assert.AreEqual(70, model.BuildList[0].BuildDurationPercent);
Assert.AreEqual("1", model.BuildList[0].BuildNumber);
@@ -350,10 +353,10 @@ public async Task AzDeploymentsControllerAPILiveIntegrationTest()
//Arrange
bool getSampleData = false;
string organization = "samsmithnz";
- string project = "SamLearnsAzure";
- string repository = "SamLearnsAzure";
- string branch = "refs/heads/master";
- string buildName = "SamLearnsAzure.CI";
+ string project = "AzDoDevOpsMetrics";
+ string repository = "AzDoDevOpsMetrics";
+ string branch = "refs/heads/main";
+ string buildName = "azure-pipelines.yml";
int numberOfDays = 7;
int maxNumberOfItems = 20;
bool useCache = false;
diff --git a/src/DevOpsMetrics.Tests/Service/LeadTimeForChangesControllerTests.cs b/src/DevOpsMetrics.Tests/Service/LeadTimeForChangesControllerTests.cs
index a2169863..c43d2c06 100644
--- a/src/DevOpsMetrics.Tests/Service/LeadTimeForChangesControllerTests.cs
+++ b/src/DevOpsMetrics.Tests/Service/LeadTimeForChangesControllerTests.cs
@@ -7,20 +7,21 @@
namespace DevOpsMetrics.Tests.Service
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ [TestCategory("IntegrationTest")]
[TestClass]
- public class LeadTimeForChangesL1s : BaseConfiguration
+ public class LeadTimeForChangesControllerTests : BaseConfiguration
{
- [TestCategory("L1Test")]
+ [TestCategory("UnitTest")]
[TestMethod]
public async Task AzLeadTimeControllerIntegrationTest()
{
//Arrange
bool getSampleData = true;
string organization = "samsmithnz";
- string project = "SamLearnsAzure";
- string repository = "SamLearnsAzure";
- string branch = "refs/heads/master";
- string buildName = "SamLearnsAzure.CI";
+ string project = "AzDoDevOpsMetrics";
+ string repository = "AzDoDevOpsMetrics";
+ string branch = "refs/heads/main";
+ string buildName = "azure-pipelines.yml";
int numberOfDays = 7;
int maxNumberOfItems = 20;
bool useCache = true;
@@ -47,13 +48,13 @@ public async Task AzLeadTimeControllerIntegrationTest()
Assert.AreEqual(1f, model.AverageBuildHours);
Assert.AreEqual(12f, model.AveragePullRequestHours);
Assert.AreEqual(13f, model.LeadTimeForChangesMetric);
- Assert.AreEqual("Elite", model.LeadTimeForChangesMetricDescription);
+ Assert.AreEqual("High", model.LeadTimeForChangesMetricDescription);
Assert.AreEqual(numberOfDays, model.NumberOfDays);
Assert.IsTrue(model.MaxNumberOfItems > 0);
Assert.IsTrue(model.TotalItems > 0);
}
- [TestCategory("L1Test")]
+ [TestCategory("UnitTest")]
[TestMethod]
public async Task GHLeadTimeControllerIntegrationTest()
{
@@ -62,10 +63,10 @@ public async Task GHLeadTimeControllerIntegrationTest()
//branch=master&workflowId=1162561&numberOfDays=30&maxNumberOfItems=20
//Arrange
bool getSampleData = true;
- string owner = "samsmithnz";
+ string owner = "DeveloperMetrics";
string repo = "devopsmetrics";
string branch = "main";
- string workflowName = "DevOpsMetrics.CICD";
+ string workflowName = "CI/CD";
string workflowId = "1162561";
int numberOfDays = 30;
int maxNumberOfItems = 20;
@@ -93,24 +94,24 @@ public async Task GHLeadTimeControllerIntegrationTest()
Assert.AreEqual(1f, model.AverageBuildHours);
Assert.AreEqual(20.33f, model.AveragePullRequestHours);
Assert.AreEqual(21.33f, model.LeadTimeForChangesMetric);
- Assert.AreEqual("Elite", model.LeadTimeForChangesMetricDescription);
+ Assert.AreEqual("High", model.LeadTimeForChangesMetricDescription);
Assert.AreEqual(numberOfDays, model.NumberOfDays);
Assert.IsTrue(model.MaxNumberOfItems > 0);
Assert.IsTrue(model.TotalItems > 0);
}
- [TestCategory("L1Test")]
+ [TestCategory("UnitTest")]
[TestMethod]
public async Task AzLeadTimeControllerAPIIntegrationTest()
{
//Arrange
bool getSampleData = true;
string organization = "samsmithnz";
- string project = "SamLearnsAzure";
- string repository = "SamLearnsAzure";
- string branch = "refs/heads/master";
- string buildName = "SamLearnsAzure.CI";
+ string project = "AzDoDevOpsMetrics";
+ string repository = "AzDoDevOpsMetrics";
+ string branch = "refs/heads/main";
+ string buildName = "azure-pipelines.yml";
int numberOfDays = 7;
int maxNumberOfItems = 20;
bool useCache = true;
@@ -137,23 +138,23 @@ public async Task AzLeadTimeControllerAPIIntegrationTest()
Assert.AreEqual(1f, model.AverageBuildHours);
Assert.AreEqual(12f, model.AveragePullRequestHours);
Assert.AreEqual(13f, model.LeadTimeForChangesMetric);
- Assert.AreEqual("Elite", model.LeadTimeForChangesMetricDescription);
+ Assert.AreEqual("High", model.LeadTimeForChangesMetricDescription);
Assert.AreEqual(numberOfDays, model.NumberOfDays);
Assert.IsTrue(model.MaxNumberOfItems > 0);
Assert.IsTrue(model.TotalItems > 0);
}
- [TestCategory("L1Test")]
+ [TestCategory("UnitTest")]
[TestMethod]
public async Task GHLeadTimeControllerAPIIntegrationTest()
{
//Arrange
bool getSampleData = true;
- string owner = "samsmithnz";
+ string owner = "DeveloperMetrics";
string repo = "devopsmetrics";
string branch = "main";
- string workflowName = "DevOpsMetrics.CICD";
+ string workflowName = "CI/CD";
string workflowId = "1162561";
int numberOfDays = 7;
int maxNumberOfItems = 20;
@@ -181,24 +182,23 @@ public async Task GHLeadTimeControllerAPIIntegrationTest()
Assert.AreEqual(1f, model.AverageBuildHours);
Assert.AreEqual(20.33f, model.AveragePullRequestHours);
Assert.AreEqual(21.33f, model.LeadTimeForChangesMetric);
- Assert.AreEqual("Elite", model.LeadTimeForChangesMetricDescription);
+ Assert.AreEqual("High", model.LeadTimeForChangesMetricDescription);
Assert.AreEqual(numberOfDays, model.NumberOfDays);
Assert.IsTrue(model.MaxNumberOfItems > 0);
Assert.IsTrue(model.TotalItems > 0);
}
- [TestCategory("L1Test")]
[TestMethod]
public async Task AzLeadTimeControllerAPILiveIntegrationTest()
{
//Arrange
bool getSampleData = false;
string organization = "samsmithnz";
- string project = "SamLearnsAzure";
- string repository = "SamLearnsAzure";
- string branch = "refs/heads/master";
- string buildName = "SamLearnsAzure.CI";
+ string project = "AzDoDevOpsMetrics";
+ string repository = "AzDoDevOpsMetrics";
+ string branch = "refs/heads/main";
+ string buildName = "azure-pipelines.yml";
int numberOfDays = 30;
int maxNumberOfItems = 20;
bool useCache = true;
@@ -241,16 +241,15 @@ public async Task AzLeadTimeControllerAPILiveIntegrationTest()
}
- [TestCategory("L1Test")]
[TestMethod]
public async Task GHLeadTimeControllerAPILiveIntegrationTest()
{
//Arrange
bool getSampleData = false;
- string owner = "samsmithnz";
+ string owner = "DeveloperMetrics";
string repo = "devopsmetrics";
string branch = "main";
- string workflowName = "DevOpsMetrics.CICD";
+ string workflowName = "CI/CD";
string workflowId = "1162561";
int numberOfDays = 20;
int maxNumberOfItems = 60;
@@ -294,7 +293,6 @@ public async Task GHLeadTimeControllerAPILiveIntegrationTest()
}
- //[TestCategory("L1Test")]
//[TestMethod]
//public async Task GHFeatureFlagsLeadTimeControllerAPILiveIntegrationTest()
//{
diff --git a/src/DevOpsMetrics.Tests/Service/MeanTimeToRestoreControllerTests.cs b/src/DevOpsMetrics.Tests/Service/MeanTimeToRestoreControllerTests.cs
index 7def1c4f..a88384e1 100644
--- a/src/DevOpsMetrics.Tests/Service/MeanTimeToRestoreControllerTests.cs
+++ b/src/DevOpsMetrics.Tests/Service/MeanTimeToRestoreControllerTests.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading.Tasks;
using DevOpsMetrics.Core.Models.Common;
using DevOpsMetrics.Service.Controllers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -6,23 +7,24 @@
namespace DevOpsMetrics.Tests.Service
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ [TestCategory("IntegrationTest")]
[TestClass]
- public class MeanTimeToRestoreL1s : BaseConfiguration
+ public class MeanTimeToRestoreControllerTests : BaseConfiguration
{
- [TestCategory("L1Test")]
+ [TestCategory("UnitTest")]
[TestMethod]
- public void AzureMTTRSampleControllerIntegrationTest()
+ public async Task AzureMTTRSampleControllerIntegrationTest()
{
//Arrange
bool getSampleData = true;
- string resourceGroupName = "SamLearnsAzureProd";
+ string resourceGroupName = "DevOpsMetrics";
DevOpsPlatform targetDevOpsPlatform = DevOpsPlatform.AzureDevOps;
int numberOfDays = 7;
int maxNumberOfItems = 20;
MeanTimeToRestoreController controller = new(base.Configuration);
//Act
- MeanTimeToRestoreModel model = controller.GetAzureMeanTimeToRestore(getSampleData, targetDevOpsPlatform, resourceGroupName, numberOfDays, maxNumberOfItems);
+ MeanTimeToRestoreModel model = await controller.GetAzureMeanTimeToRestore(getSampleData, targetDevOpsPlatform, resourceGroupName, numberOfDays, maxNumberOfItems);
//Assert
Assert.IsTrue(model != null);
@@ -44,20 +46,19 @@ public void AzureMTTRSampleControllerIntegrationTest()
Assert.IsTrue(model.TotalItems > 0);
}
- [TestCategory("L1Test")]
[TestMethod]
- public void AzureMTTRsAPIControllerIntegrationTest()
+ public async Task AzureMTTRsAPIControllerIntegrationTest()
{
//Arrange
bool getSampleData = false;
- string resourceGroupName = "SamLearnsAzureProd";
+ string resourceGroupName = "DevOpsMetrics";
DevOpsPlatform targetDevOpsPlatform = DevOpsPlatform.AzureDevOps;
int numberOfDays = 60;
int maxNumberOfItems = 20;
MeanTimeToRestoreController controller = new(base.Configuration);
//Act
- MeanTimeToRestoreModel model = controller.GetAzureMeanTimeToRestore(getSampleData, targetDevOpsPlatform, resourceGroupName, numberOfDays, maxNumberOfItems);
+ MeanTimeToRestoreModel model = await controller.GetAzureMeanTimeToRestore(getSampleData, targetDevOpsPlatform, resourceGroupName, numberOfDays, maxNumberOfItems);
//Assert
Assert.IsTrue(model != null);
@@ -82,9 +83,8 @@ public void AzureMTTRsAPIControllerIntegrationTest()
Assert.IsTrue(model.TotalItems >= 0);
}
- [TestCategory("L1Test")]
[TestMethod]
- public void AzureMTTRsAPINullIntegrationTest()
+ public async Task AzureMTTRsAPINullIntegrationTest()
{
//Arrange
bool getSampleData = false;
@@ -95,7 +95,7 @@ public void AzureMTTRsAPINullIntegrationTest()
MeanTimeToRestoreController controller = new(base.Configuration);
//Act
- MeanTimeToRestoreModel model = controller.GetAzureMeanTimeToRestore(getSampleData, targetDevOpsPlatform, resourceGroupName, numberOfDays, maxNumberOfItems);
+ MeanTimeToRestoreModel model = await controller.GetAzureMeanTimeToRestore(getSampleData, targetDevOpsPlatform, resourceGroupName, numberOfDays, maxNumberOfItems);
//Assert
Assert.IsTrue(model != null);
@@ -120,9 +120,8 @@ public void AzureMTTRsAPINullIntegrationTest()
Assert.IsTrue(model.TotalItems >= 0);
}
- [TestCategory("L1Test")]
[TestMethod]
- public void AzureMTTRsAPIEmptyIntegrationTest()
+ public async Task AzureMTTRsAPIEmptyIntegrationTest()
{
//Arrange
bool getSampleData = false;
@@ -133,7 +132,7 @@ public void AzureMTTRsAPIEmptyIntegrationTest()
MeanTimeToRestoreController controller = new(base.Configuration);
//Act
- MeanTimeToRestoreModel model = controller.GetAzureMeanTimeToRestore(getSampleData, targetDevOpsPlatform, resourceGroupName, numberOfDays, maxNumberOfItems);
+ MeanTimeToRestoreModel model = await controller.GetAzureMeanTimeToRestore(getSampleData, targetDevOpsPlatform, resourceGroupName, numberOfDays, maxNumberOfItems);
//Assert
Assert.IsTrue(model != null);
diff --git a/src/DevOpsMetrics.Tests/Service/NightlyProcessTests.cs b/src/DevOpsMetrics.Tests/Service/NightlyProcessTests.cs
new file mode 100644
index 00000000..87d5144d
--- /dev/null
+++ b/src/DevOpsMetrics.Tests/Service/NightlyProcessTests.cs
@@ -0,0 +1,114 @@
+//using System.Collections.Generic;
+//using System.Threading.Tasks;
+//using DevOpsMetrics.Core.DataAccess.TableStorage;
+//using DevOpsMetrics.Core.Models.Common;
+//using DevOpsMetrics.Core.Models.GitHub;
+//using DevOpsMetrics.Service;
+//using DevOpsMetrics.Service.Controllers;
+//using Microsoft.Extensions.Logging;
+//using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+//namespace DevOpsMetrics.Tests.Service
+//{
+// [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+// [TestCategory("IntegrationTest")]
+// [TestClass]
+// public class NightlyProcessTests : BaseConfiguration
+// {
+// [TestMethod]
+// public async Task GitHubProcessingTest()
+// {
+// //Arrange
+// string clientId = Configuration["AppSettings:GitHubClientId"];
+// string clientSecret = Configuration["AppSettings:GitHubClientSecret"];
+// ProcessingResult result = null;
+// AzureTableStorageDA da = new();
+// BuildsController buildsController = new(Configuration, da);
+// PullRequestsController pullRequestsController = new(Configuration, da);
+// SettingsController settingsController = new(Configuration, da);
+// using var loggerFactory = LoggerFactory.Create(loggingBuilder => loggingBuilder
+// .SetMinimumLevel(LogLevel.Trace)
+// .AddConsole());
+// ILogger log = loggerFactory.CreateLogger();
+// TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
+
+// //Act
+// List results = da.GetGitHubSettingsFromStorage(tableStorageConfig, tableStorageConfig.TableGitHubSettings, null);
+// foreach (GitHubSettings item in results)
+// {
+// if (item.Repo == "AzurePipelinesToGitHubActionsConverter")
+// {
+// result = await Processing.ProcessGitHubItem(item, clientId, clientSecret, tableStorageConfig,
+// 30, 20,
+// buildsController, pullRequestsController, settingsController,
+// log, 0);
+// }
+// }
+
+// //Assert
+// Assert.IsNotNull(result);
+// }
+
+
+
+// //[TestMethod]
+// //public async Task GHUpdateAzurePipelinesToGitHubActionsConverterWebSettingDAIntegrationTest()
+// //{
+// // //Arrange
+// // TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(base.Configuration);
+// // string owner = "samsmithnz";
+// // string repo = "AzurePipelinesToGitHubActionsConverterWeb";
+// // string branch = "main";
+// // string workflowName = "Pipelines to Actions website CI/CD";
+// // string workflowId = "43084";
+// // string resourceGroupName = "PipelinesToActions";
+// // int itemOrder = 3;
+// // bool showSetting = true;
+
+// // //Act
+// // AzureTableStorageDA da = new();
+// // bool result = await da.UpdateGitHubSettingInStorage(tableStorageConfig, tableStorageConfig.TableGitHubSettings,
+// // owner, repo, branch, workflowName, workflowId, resourceGroupName, itemOrder, showSetting);
+
+// // //Assert
+// // Assert.IsTrue(result);
+// //}
+
+
+// //[TestMethod]
+// //public async Task GHUpdateAllDevOpsMetricsIntegrationTest()
+// //{
+// // //Arrange
+// // string clientId = Configuration["AppSettings:GitHubClientId"];
+// // string clientSecret = Configuration["AppSettings:GitHubClientSecret"];
+// // int numberOfDays = 30;
+// // int maxNumberOfItems = 20;
+// // int totalResults = 0;
+// // using var loggerFactory = LoggerFactory.Create(loggingBuilder => loggingBuilder
+// // .SetMinimumLevel(LogLevel.Trace)
+// // .AddConsole());
+// // ILogger log = loggerFactory.CreateLogger();
+// // TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
+
+// // //Act
+// // AzureTableStorageDA azureTableStorageDA = new();
+// // BuildsController buildsController = new(Configuration, azureTableStorageDA);
+// // PullRequestsController pullRequestsController = new(Configuration, azureTableStorageDA);
+// // SettingsController settingsController = new(Configuration, azureTableStorageDA);
+// // List ghSettings = settingsController.GetGitHubSettings();
+
+// // foreach (GitHubSettings item in ghSettings)
+// // {
+// // ProcessingResult ghResult = await Processing.ProcessGitHubItem(item,
+// // clientId, clientSecret, tableStorageConfig,
+// // numberOfDays, maxNumberOfItems,
+// // buildsController, pullRequestsController, settingsController, log, totalResults);
+// // totalResults = ghResult.TotalResults;
+// // }
+
+// // //Assert
+// // Assert.IsTrue(totalResults >= 0);
+// //}
+
+// }
+//}
diff --git a/src/DevOpsMetrics.Tests/Service/SecretsProcessingTests.cs b/src/DevOpsMetrics.Tests/Service/SecretsProcessingTests.cs
index d1f27809..2de03226 100644
--- a/src/DevOpsMetrics.Tests/Service/SecretsProcessingTests.cs
+++ b/src/DevOpsMetrics.Tests/Service/SecretsProcessingTests.cs
@@ -4,7 +4,7 @@
namespace DevOpsMetrics.Tests.Service
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L0Test")]
+ [TestCategory("UnitTest")]
[TestClass]
public class SecretsProcessingTests
{
@@ -12,65 +12,65 @@ public class SecretsProcessingTests
public void SecretWithDotTest()
{
//Arrange
- string name = "SamLearnsAzure.CI";
+ string name = "Secret.CI";
//Act
string result = SecretsProcessing.CleanKey(name);
//Assert
- Assert.AreEqual("SamLearnsAzure-CI", result);
+ Assert.AreEqual("Secret-CI", result);
}
[TestMethod]
public void SecretWithSpaceTest()
{
//Arrange
- string name = "SamLearnsAzure CI";
+ string name = "Secret CI";
//Act
string result = SecretsProcessing.CleanKey(name);
//Assert
- Assert.AreEqual("SamLearnsAzure-CI", result);
+ Assert.AreEqual("Secret-CI", result);
}
[TestMethod]
public void SecretWithColonTest()
{
//Arrange
- string name = "SamLearnsAzure:CI";
+ string name = "Secret:CI";
//Act
string result = SecretsProcessing.CleanKey(name);
//Assert
- Assert.AreEqual("SamLearnsAzure-CI", result);
+ Assert.AreEqual("Secret-CI", result);
}
[TestMethod]
public void SecretWithQuestionTest()
{
//Arrange
- string name = "SamLearnsAzure?CI";
+ string name = "Secret?CI";
//Act
string result = SecretsProcessing.CleanKey(name);
//Assert
- Assert.AreEqual("SamLearnsAzure-CI", result);
+ Assert.AreEqual("Secret-CI", result);
}
[TestMethod]
public void SecretThatIsValidTest()
{
//Arrange
- string name = "SamLearnsAzure123abcCI";
+ string name = "Secret123abcCI";
//Act
string result = SecretsProcessing.CleanKey(name);
//Assert
- Assert.AreEqual("SamLearnsAzure123abcCI", result);
+ Assert.AreEqual("Secret123abcCI", result);
}
}
diff --git a/src/DevOpsMetrics.Tests/Service/TableStorageControllerDATests.cs b/src/DevOpsMetrics.Tests/Service/TableStorageControllerDATests.cs
index 5d7d9ae6..9fcb6298 100644
--- a/src/DevOpsMetrics.Tests/Service/TableStorageControllerDATests.cs
+++ b/src/DevOpsMetrics.Tests/Service/TableStorageControllerDATests.cs
@@ -7,12 +7,12 @@
using DevOpsMetrics.Service.Controllers;
using Microsoft.Extensions.Configuration;
using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Moq;
+using NSubstitute;
namespace DevOpsMetrics.Tests.Service
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- [TestCategory("L0Test")]
+ [TestCategory("UnitTest")]
[TestClass]
public class TableStorageControllerDATests
{
@@ -51,10 +51,10 @@ public void TestStart()
public async Task UpdateAzureDevOpsBuildsTest()
{
//Arrange
- Mock mockDA = new();
- mockDA.Setup(repo => repo.UpdateAzureDevOpsBuildsInStorage(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(Task.FromResult(GetSampleUpdateData()));
- mockDA.Setup(repo => repo.GetAzureDevOpsSettingsFromStorage(It.IsAny(), It.IsAny(), It.IsAny())).Returns(new List { new AzureDevOpsSettings() });
- BuildsController controller = new(_configuration, mockDA.Object);
+ IAzureTableStorageDA mockDA = Substitute.For();
+ mockDA.UpdateAzureDevOpsBuildsInStorage(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()).Returns(Task.FromResult(GetSampleUpdateData()));
+ mockDA.GetAzureDevOpsSettingsFromStorage(Arg.Any(), Arg.Any(), Arg.Any()).Returns(Task.FromResult(new List { new AzureDevOpsSettings() }));
+ BuildsController controller = new(_configuration, mockDA);
string organization = "";
string project = "";
string repository = "";
@@ -75,10 +75,10 @@ public async Task UpdateAzureDevOpsBuildsTest()
public async Task UpdateGitHubActionRunsTest()
{
//Arrange
- Mock mockDA = new();
- mockDA.Setup(repo => repo.UpdateGitHubActionRunsInStorage(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny