mirror of
https://dev.azure.com/effectory/Survey%20Software/_git/Cloud%20Engineering
synced 2026-02-27 18:52:18 +01:00
Merged PR 63702: Add Sonar Client to update permissions and tags in Sonar Projects to new team structure
Add Sonar Client to update permissions and tags in Sonar Projects to new team structure Related work items: #125680
This commit is contained in:
38
.aiignore
Normal file
38
.aiignore
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Ignore environment and config files
|
||||||
|
environment.ts
|
||||||
|
environment.*.ts
|
||||||
|
environment.prod.ts
|
||||||
|
config.json
|
||||||
|
local.settings.json
|
||||||
|
local.settings.Designer.cs
|
||||||
|
settings.json
|
||||||
|
.env
|
||||||
|
auth-config.ts
|
||||||
|
assets/config/
|
||||||
|
proxy.conf.json
|
||||||
|
package.json
|
||||||
|
angular.json
|
||||||
|
appsettings.json
|
||||||
|
appsettings.*.json
|
||||||
|
web.config
|
||||||
|
app.config
|
||||||
|
secrets.json
|
||||||
|
.env
|
||||||
|
launchSettings.json
|
||||||
|
Properties/PublishProfiles/*.pubxml
|
||||||
|
ServiceConfiguration.*.cscfg
|
||||||
|
App_Data/
|
||||||
|
*.csproj
|
||||||
|
*.mdf
|
||||||
|
*.pfx
|
||||||
|
*.p12
|
||||||
|
*.crt
|
||||||
|
*.key
|
||||||
|
.DS_Store
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
out/
|
||||||
334
.gitignore
vendored
334
.gitignore
vendored
@@ -1,3 +1,337 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# Bicep output
|
||||||
|
Authorization.Deploy/authorization.json
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
#**/Properties/launchSettings.json
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# CodeRush
|
||||||
|
.cr/
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
Powershell/Tools/temp.ps1
|
Powershell/Tools/temp.ps1
|
||||||
Powershell/Tools/temp.json
|
Powershell/Tools/temp.json
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
|
|||||||
334
ConsoleApps/.gitignore
vendored
334
ConsoleApps/.gitignore
vendored
@@ -1,334 +0,0 @@
|
|||||||
## Ignore Visual Studio temporary files, build results, and
|
|
||||||
## files generated by popular Visual Studio add-ons.
|
|
||||||
##
|
|
||||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
|
||||||
|
|
||||||
# Bicep output
|
|
||||||
Authorization.Deploy/authorization.json
|
|
||||||
|
|
||||||
# User-specific files
|
|
||||||
*.suo
|
|
||||||
*.user
|
|
||||||
*.userosscache
|
|
||||||
*.sln.docstates
|
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
|
||||||
*.userprefs
|
|
||||||
|
|
||||||
# Build results
|
|
||||||
[Dd]ebug/
|
|
||||||
[Dd]ebugPublic/
|
|
||||||
[Rr]elease/
|
|
||||||
[Rr]eleases/
|
|
||||||
x64/
|
|
||||||
x86/
|
|
||||||
bld/
|
|
||||||
[Bb]in/
|
|
||||||
[Oo]bj/
|
|
||||||
[Ll]og/
|
|
||||||
|
|
||||||
# Visual Studio 2015/2017 cache/options directory
|
|
||||||
.vs/
|
|
||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
|
||||||
#wwwroot/
|
|
||||||
|
|
||||||
# Visual Studio 2017 auto generated files
|
|
||||||
Generated\ Files/
|
|
||||||
|
|
||||||
# MSTest test Results
|
|
||||||
[Tt]est[Rr]esult*/
|
|
||||||
[Bb]uild[Ll]og.*
|
|
||||||
|
|
||||||
# NUNIT
|
|
||||||
*.VisualState.xml
|
|
||||||
TestResult.xml
|
|
||||||
|
|
||||||
# Build Results of an ATL Project
|
|
||||||
[Dd]ebugPS/
|
|
||||||
[Rr]eleasePS/
|
|
||||||
dlldata.c
|
|
||||||
|
|
||||||
# Benchmark Results
|
|
||||||
BenchmarkDotNet.Artifacts/
|
|
||||||
|
|
||||||
# .NET Core
|
|
||||||
project.lock.json
|
|
||||||
project.fragment.lock.json
|
|
||||||
artifacts/
|
|
||||||
#**/Properties/launchSettings.json
|
|
||||||
|
|
||||||
# StyleCop
|
|
||||||
StyleCopReport.xml
|
|
||||||
|
|
||||||
# Files built by Visual Studio
|
|
||||||
*_i.c
|
|
||||||
*_p.c
|
|
||||||
*_i.h
|
|
||||||
*.ilk
|
|
||||||
*.meta
|
|
||||||
*.obj
|
|
||||||
*.iobj
|
|
||||||
*.pch
|
|
||||||
*.pdb
|
|
||||||
*.ipdb
|
|
||||||
*.pgc
|
|
||||||
*.pgd
|
|
||||||
*.rsp
|
|
||||||
*.sbr
|
|
||||||
*.tlb
|
|
||||||
*.tli
|
|
||||||
*.tlh
|
|
||||||
*.tmp
|
|
||||||
*.tmp_proj
|
|
||||||
*.log
|
|
||||||
*.vspscc
|
|
||||||
*.vssscc
|
|
||||||
.builds
|
|
||||||
*.pidb
|
|
||||||
*.svclog
|
|
||||||
*.scc
|
|
||||||
|
|
||||||
# Chutzpah Test files
|
|
||||||
_Chutzpah*
|
|
||||||
|
|
||||||
# Visual C++ cache files
|
|
||||||
ipch/
|
|
||||||
*.aps
|
|
||||||
*.ncb
|
|
||||||
*.opendb
|
|
||||||
*.opensdf
|
|
||||||
*.sdf
|
|
||||||
*.cachefile
|
|
||||||
*.VC.db
|
|
||||||
*.VC.VC.opendb
|
|
||||||
|
|
||||||
# Visual Studio profiler
|
|
||||||
*.psess
|
|
||||||
*.vsp
|
|
||||||
*.vspx
|
|
||||||
*.sap
|
|
||||||
|
|
||||||
# Visual Studio Trace Files
|
|
||||||
*.e2e
|
|
||||||
|
|
||||||
# TFS 2012 Local Workspace
|
|
||||||
$tf/
|
|
||||||
|
|
||||||
# Guidance Automation Toolkit
|
|
||||||
*.gpState
|
|
||||||
|
|
||||||
# ReSharper is a .NET coding add-in
|
|
||||||
_ReSharper*/
|
|
||||||
*.[Rr]e[Ss]harper
|
|
||||||
*.DotSettings.user
|
|
||||||
|
|
||||||
# JustCode is a .NET coding add-in
|
|
||||||
.JustCode
|
|
||||||
|
|
||||||
# TeamCity is a build add-in
|
|
||||||
_TeamCity*
|
|
||||||
|
|
||||||
# DotCover is a Code Coverage Tool
|
|
||||||
*.dotCover
|
|
||||||
|
|
||||||
# AxoCover is a Code Coverage Tool
|
|
||||||
.axoCover/*
|
|
||||||
!.axoCover/settings.json
|
|
||||||
|
|
||||||
# Visual Studio code coverage results
|
|
||||||
*.coverage
|
|
||||||
*.coveragexml
|
|
||||||
|
|
||||||
# NCrunch
|
|
||||||
_NCrunch_*
|
|
||||||
.*crunch*.local.xml
|
|
||||||
nCrunchTemp_*
|
|
||||||
|
|
||||||
# MightyMoose
|
|
||||||
*.mm.*
|
|
||||||
AutoTest.Net/
|
|
||||||
|
|
||||||
# Web workbench (sass)
|
|
||||||
.sass-cache/
|
|
||||||
|
|
||||||
# Installshield output folder
|
|
||||||
[Ee]xpress/
|
|
||||||
|
|
||||||
# DocProject is a documentation generator add-in
|
|
||||||
DocProject/buildhelp/
|
|
||||||
DocProject/Help/*.HxT
|
|
||||||
DocProject/Help/*.HxC
|
|
||||||
DocProject/Help/*.hhc
|
|
||||||
DocProject/Help/*.hhk
|
|
||||||
DocProject/Help/*.hhp
|
|
||||||
DocProject/Help/Html2
|
|
||||||
DocProject/Help/html
|
|
||||||
|
|
||||||
# Click-Once directory
|
|
||||||
publish/
|
|
||||||
|
|
||||||
# Publish Web Output
|
|
||||||
*.[Pp]ublish.xml
|
|
||||||
*.azurePubxml
|
|
||||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
|
||||||
# but database connection strings (with potential passwords) will be unencrypted
|
|
||||||
*.pubxml
|
|
||||||
*.publishproj
|
|
||||||
|
|
||||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
|
||||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
|
||||||
# in these scripts will be unencrypted
|
|
||||||
PublishScripts/
|
|
||||||
|
|
||||||
# NuGet Packages
|
|
||||||
*.nupkg
|
|
||||||
# The packages folder can be ignored because of Package Restore
|
|
||||||
**/[Pp]ackages/*
|
|
||||||
# except build/, which is used as an MSBuild target.
|
|
||||||
!**/[Pp]ackages/build/
|
|
||||||
# Uncomment if necessary however generally it will be regenerated when needed
|
|
||||||
#!**/[Pp]ackages/repositories.config
|
|
||||||
# NuGet v3's project.json files produces more ignorable files
|
|
||||||
*.nuget.props
|
|
||||||
*.nuget.targets
|
|
||||||
|
|
||||||
# Microsoft Azure Build Output
|
|
||||||
csx/
|
|
||||||
*.build.csdef
|
|
||||||
|
|
||||||
# Microsoft Azure Emulator
|
|
||||||
ecf/
|
|
||||||
rcf/
|
|
||||||
|
|
||||||
# Windows Store app package directories and files
|
|
||||||
AppPackages/
|
|
||||||
BundleArtifacts/
|
|
||||||
Package.StoreAssociation.xml
|
|
||||||
_pkginfo.txt
|
|
||||||
*.appx
|
|
||||||
|
|
||||||
# Visual Studio cache files
|
|
||||||
# files ending in .cache can be ignored
|
|
||||||
*.[Cc]ache
|
|
||||||
# but keep track of directories ending in .cache
|
|
||||||
!*.[Cc]ache/
|
|
||||||
|
|
||||||
# Others
|
|
||||||
ClientBin/
|
|
||||||
~$*
|
|
||||||
*~
|
|
||||||
*.dbmdl
|
|
||||||
*.dbproj.schemaview
|
|
||||||
*.jfm
|
|
||||||
*.pfx
|
|
||||||
*.publishsettings
|
|
||||||
orleans.codegen.cs
|
|
||||||
|
|
||||||
# Including strong name files can present a security risk
|
|
||||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
|
||||||
#*.snk
|
|
||||||
|
|
||||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
|
||||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
|
||||||
#bower_components/
|
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
|
||||||
Generated_Code/
|
|
||||||
|
|
||||||
# Backup & report files from converting an old project file
|
|
||||||
# to a newer Visual Studio version. Backup files are not needed,
|
|
||||||
# because we have git ;-)
|
|
||||||
_UpgradeReport_Files/
|
|
||||||
Backup*/
|
|
||||||
UpgradeLog*.XML
|
|
||||||
UpgradeLog*.htm
|
|
||||||
ServiceFabricBackup/
|
|
||||||
*.rptproj.bak
|
|
||||||
|
|
||||||
# SQL Server files
|
|
||||||
*.mdf
|
|
||||||
*.ldf
|
|
||||||
*.ndf
|
|
||||||
|
|
||||||
# Business Intelligence projects
|
|
||||||
*.rdl.data
|
|
||||||
*.bim.layout
|
|
||||||
*.bim_*.settings
|
|
||||||
*.rptproj.rsuser
|
|
||||||
|
|
||||||
# Microsoft Fakes
|
|
||||||
FakesAssemblies/
|
|
||||||
|
|
||||||
# GhostDoc plugin setting file
|
|
||||||
*.GhostDoc.xml
|
|
||||||
|
|
||||||
# Node.js Tools for Visual Studio
|
|
||||||
.ntvs_analysis.dat
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# Visual Studio 6 build log
|
|
||||||
*.plg
|
|
||||||
|
|
||||||
# Visual Studio 6 workspace options file
|
|
||||||
*.opt
|
|
||||||
|
|
||||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
|
||||||
*.vbw
|
|
||||||
|
|
||||||
# Visual Studio LightSwitch build output
|
|
||||||
**/*.HTMLClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/ModelManifest.xml
|
|
||||||
**/*.Server/GeneratedArtifacts
|
|
||||||
**/*.Server/ModelManifest.xml
|
|
||||||
_Pvt_Extensions
|
|
||||||
|
|
||||||
# Paket dependency manager
|
|
||||||
.paket/paket.exe
|
|
||||||
paket-files/
|
|
||||||
|
|
||||||
# FAKE - F# Make
|
|
||||||
.fake/
|
|
||||||
|
|
||||||
# JetBrains Rider
|
|
||||||
.idea/
|
|
||||||
*.sln.iml
|
|
||||||
|
|
||||||
# CodeRush
|
|
||||||
.cr/
|
|
||||||
|
|
||||||
# Python Tools for Visual Studio (PTVS)
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
|
|
||||||
# Cake - Uncomment if you are using it
|
|
||||||
# tools/**
|
|
||||||
# !tools/packages.config
|
|
||||||
|
|
||||||
# Tabs Studio
|
|
||||||
*.tss
|
|
||||||
|
|
||||||
# Telerik's JustMock configuration file
|
|
||||||
*.jmconfig
|
|
||||||
|
|
||||||
# BizTalk build output
|
|
||||||
*.btp.cs
|
|
||||||
*.btm.cs
|
|
||||||
*.odx.cs
|
|
||||||
*.xsd.cs
|
|
||||||
|
|
||||||
# OpenCover UI analysis results
|
|
||||||
OpenCover/
|
|
||||||
|
|
||||||
# Azure Stream Analytics local run output
|
|
||||||
ASALocalRun/
|
|
||||||
|
|
||||||
# MSBuild Binary and Structured Log
|
|
||||||
*.binlog
|
|
||||||
|
|
||||||
# NVidia Nsight GPU debugger configuration file
|
|
||||||
*.nvuser
|
|
||||||
|
|
||||||
# MFractors (Xamarin productivity tool) working folder
|
|
||||||
.mfractor/
|
|
||||||
|
|
||||||
@@ -2,20 +2,20 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Azure.Identity" Version="1.6.0" />
|
<PackageReference Include="Azure.Identity" Version="1.17.0" />
|
||||||
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.3.0" />
|
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.8.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.2.9" />
|
<PackageReference Include="Microsoft.Identity.Client" Version="4.78.0" />
|
||||||
<PackageReference Include="Spectre.Console" Version="0.44.0" />
|
<PackageReference Include="Spectre.Console" Version="0.53.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
namespace AzureRestApi.Models.Api
|
namespace AzureRestApi.Models.Api;
|
||||||
{
|
|
||||||
public class ApiResponse<T>
|
public class ApiResponse<T>
|
||||||
{
|
{
|
||||||
public List<T>? value { get; set; }
|
public List<T>? value { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
using AzureRestApi.Models.Resources;
|
using AzureRestApi.Models.Resources;
|
||||||
|
|
||||||
namespace AzureRestApi.Models.Api
|
namespace AzureRestApi.Models.Api;
|
||||||
{
|
|
||||||
public class TagProperties
|
public class TagProperties
|
||||||
{
|
{
|
||||||
public TagTags properties { get; set; } = new TagTags();
|
public TagTags properties { get; set; } = new TagTags();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace AzureRestApi.Models.Resources
|
namespace AzureRestApi.Models.Resources;
|
||||||
{
|
|
||||||
public class Deployment
|
public class Deployment
|
||||||
{
|
{
|
||||||
public string? id { get; set; }
|
public string? id { get; set; }
|
||||||
@@ -8,4 +8,3 @@
|
|||||||
public string? location { get; set; }
|
public string? location { get; set; }
|
||||||
public DeploymentProperties? properties { get; set; }
|
public DeploymentProperties? properties { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace AzureRestApi.Models.Resources
|
namespace AzureRestApi.Models.Resources;
|
||||||
{
|
|
||||||
public class DeploymentProperties
|
public class DeploymentProperties
|
||||||
{
|
{
|
||||||
public string? templateHash { get; set; }
|
public string? templateHash { get; set; }
|
||||||
@@ -14,4 +14,3 @@
|
|||||||
//public string? outputs { get; set; }
|
//public string? outputs { get; set; }
|
||||||
public List<Resource>? outputResources { get; set; }
|
public List<Resource>? outputResources { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace AzureRestApi.Models.Resources
|
namespace AzureRestApi.Models.Resources;
|
||||||
{
|
|
||||||
public class Resource
|
public class Resource
|
||||||
{
|
{
|
||||||
public string? id { get; set; }
|
public string? id { get; set; }
|
||||||
@@ -25,4 +25,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace AzureRestApi.Models.Resources
|
namespace AzureRestApi.Models.Resources;
|
||||||
{
|
|
||||||
public class ResourceGroup
|
public class ResourceGroup
|
||||||
{
|
{
|
||||||
public string? id { get; set; }
|
public string? id { get; set; }
|
||||||
@@ -9,4 +9,3 @@
|
|||||||
public Dictionary<string, string> tags { get; set; } = new Dictionary<string, string>();
|
public Dictionary<string, string> tags { get; set; } = new Dictionary<string, string>();
|
||||||
public DeploymentProperties? properties { get; set; }
|
public DeploymentProperties? properties { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
namespace AzureRestApi.Models.Resources
|
namespace AzureRestApi.Models.Resources;
|
||||||
{
|
|
||||||
public class ResourceGroupProperties
|
public class ResourceGroupProperties
|
||||||
{
|
{
|
||||||
public string? provisioningState { get; set; }
|
public string? provisioningState { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace AzureRestApi.Models.Resources
|
namespace AzureRestApi.Models.Resources;
|
||||||
{
|
|
||||||
public class Subscription
|
public class Subscription
|
||||||
{
|
{
|
||||||
public string? id { get; set; }
|
public string? id { get; set; }
|
||||||
@@ -17,4 +17,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
namespace AzureRestApi.Models.Resources
|
namespace AzureRestApi.Models.Resources;
|
||||||
{
|
|
||||||
public class TagTags
|
public class TagTags
|
||||||
{
|
{
|
||||||
public Dictionary<string, string> tags { get; set; } = new Dictionary<string, string>();
|
public Dictionary<string, string> tags { get; set; } = new Dictionary<string, string>();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
namespace AzureRestApi.Models
|
namespace AzureRestApi.Models;
|
||||||
{
|
|
||||||
public class Settings
|
public class Settings
|
||||||
{
|
{
|
||||||
public string KeyVaultName { get; set; } = string.Empty;
|
public string KeyVaultName { get; set; } = string.Empty;
|
||||||
public string AzureTenantId { get; set; } = string.Empty;
|
public string AzureTenantId { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
namespace AzureRestApi
|
namespace AzureRestApi;
|
||||||
{
|
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
static Task Main(string[] args) =>
|
static Task Main(string[] args) =>
|
||||||
@@ -14,7 +14,7 @@ namespace AzureRestApi
|
|||||||
|
|
||||||
static IHostBuilder CreateHostBuilder(string[] args) =>
|
static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
Host.CreateDefaultBuilder(args)
|
Host.CreateDefaultBuilder(args)
|
||||||
.ConfigureServices((builder, services) =>
|
.ConfigureServices((_, services) =>
|
||||||
{
|
{
|
||||||
IConfiguration config = new ConfigurationBuilder()
|
IConfiguration config = new ConfigurationBuilder()
|
||||||
.AddJsonFile("appsettings.json")
|
.AddJsonFile("appsettings.json")
|
||||||
@@ -38,4 +38,3 @@ namespace AzureRestApi
|
|||||||
services.AddHostedService<OptionService>();
|
services.AddHostedService<OptionService>();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -1,40 +1,40 @@
|
|||||||
using Azure.Identity;
|
using Azure.Identity;
|
||||||
using Azure.Security.KeyVault.Secrets;
|
using Azure.Security.KeyVault.Secrets;
|
||||||
using AzureRestApi.Models;
|
using AzureRestApi.Models;
|
||||||
using Microsoft.IdentityModel.Clients.ActiveDirectory;
|
using Microsoft.Identity.Client;
|
||||||
|
|
||||||
namespace AzureRestApi.Repositories
|
namespace AzureRestApi.Repositories;
|
||||||
|
|
||||||
|
public class AccessTokenRepository(Settings settings)
|
||||||
{
|
{
|
||||||
public class AccessTokenRepository
|
|
||||||
{
|
|
||||||
private readonly Settings _settings;
|
|
||||||
private string? _accessToken;
|
private string? _accessToken;
|
||||||
|
|
||||||
public AccessTokenRepository(Settings settings)
|
|
||||||
{
|
|
||||||
_settings = settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetAccessToken()
|
public async Task<string> GetAccessToken()
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(_accessToken)) return _accessToken;
|
if (!string.IsNullOrWhiteSpace(_accessToken)) return _accessToken;
|
||||||
|
|
||||||
var keyvaultUri = "https://" + _settings.KeyVaultName + ".vault.azure.net";
|
var keyVaultUri = "https://" + settings.KeyVaultName + ".vault.azure.net";
|
||||||
var credential = new DefaultAzureCredential();
|
var credential = new DefaultAzureCredential();
|
||||||
var client = new SecretClient(new Uri(keyvaultUri), credential);
|
var client = new SecretClient(new Uri(keyVaultUri), credential);
|
||||||
var clientId = (await client.GetSecretAsync("ClientID")).Value.Value;
|
var clientId = (await client.GetSecretAsync("ClientID")).Value.Value;
|
||||||
var clientSecret = (await client.GetSecretAsync("ClientSecret")).Value.Value;
|
var clientSecret = (await client.GetSecretAsync("ClientSecret")).Value.Value;
|
||||||
|
|
||||||
ClientCredential cc = new(clientId, clientSecret);
|
var app = ConfidentialClientApplicationBuilder
|
||||||
var context = new AuthenticationContext("https://login.microsoftonline.com/" + _settings.AzureTenantId);
|
.Create(clientId)
|
||||||
var result = context.AcquireTokenAsync("https://management.azure.com/", cc);
|
.WithClientSecret(clientSecret)
|
||||||
|
.WithAuthority(new Uri($"https://login.microsoftonline.com/{settings.AzureTenantId}"))
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var scopes = new[] {"https://management.azure.com/.default"};
|
||||||
|
|
||||||
|
var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
|
||||||
if (result == null)
|
if (result == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Failed to obtain the Access token");
|
throw new InvalidOperationException("Failed to obtain the Access token");
|
||||||
}
|
}
|
||||||
_accessToken = result.Result.AccessToken;
|
|
||||||
|
_accessToken = result.AccessToken;
|
||||||
|
|
||||||
return _accessToken;
|
return _accessToken;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,29 +2,20 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace AzureRestApi.Repositories
|
namespace AzureRestApi.Repositories;
|
||||||
{
|
|
||||||
public abstract class AzureBaseRepository<T>
|
|
||||||
{
|
|
||||||
private readonly HttpClient _httpClient;
|
|
||||||
private readonly AccessTokenRepository _accessTokenRepository;
|
|
||||||
|
|
||||||
public AzureBaseRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository)
|
public abstract class AzureBaseRepository<T>(HttpClient httpClient, AccessTokenRepository accessTokenRepository)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
|
||||||
_accessTokenRepository = accessTokenRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task<List<T>> GetAllByUri(Uri uri)
|
protected async Task<List<T>> GetAllByUri(Uri uri)
|
||||||
{
|
{
|
||||||
var accessToken = await _accessTokenRepository.GetAccessToken();
|
var accessToken = await accessTokenRepository.GetAccessToken();
|
||||||
|
|
||||||
HttpRequestMessage request = new(HttpMethod.Get, uri);
|
HttpRequestMessage request = new(HttpMethod.Get, uri);
|
||||||
request.Headers.Accept.Clear();
|
request.Headers.Accept.Clear();
|
||||||
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
|
||||||
|
|
||||||
var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
|
var response = await httpClient.SendAsync(request).ConfigureAwait(false);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -32,13 +23,12 @@ namespace AzureRestApi.Repositories
|
|||||||
|
|
||||||
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
var result = JsonSerializer.Deserialize<ApiResponse<T>>(responseString)?.value;
|
var result = JsonSerializer.Deserialize<ApiResponse<T>>(responseString)?.value;
|
||||||
return result ?? new List<T>();
|
return result ?? [];
|
||||||
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
return new List<T>();
|
return [];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
using AzureRestApi.Models.Resources;
|
using AzureRestApi.Models.Resources;
|
||||||
|
|
||||||
namespace AzureRestApi.Repositories
|
namespace AzureRestApi.Repositories;
|
||||||
{
|
|
||||||
public class AzureDeploymentRepository : AzureBaseRepository<Deployment>
|
|
||||||
{
|
|
||||||
public AzureDeploymentRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository) : base(httpClient, accessTokenRepository)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public class AzureDeploymentRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository)
|
||||||
|
: AzureBaseRepository<Deployment>(httpClient, accessTokenRepository)
|
||||||
|
{
|
||||||
public async Task<List<Deployment>> GetAllBySubscription(string subscriptionId)
|
public async Task<List<Deployment>> GetAllBySubscription(string subscriptionId)
|
||||||
{
|
{
|
||||||
// GET https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Resources/deployments/?api-version=2021-04-01
|
// GET https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Resources/deployments/?api-version=2021-04-01
|
||||||
@@ -20,6 +17,4 @@ namespace AzureRestApi.Repositories
|
|||||||
var uri = new Uri($"https://management.azure.com/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/deployments/?api-version=2021-04-01");
|
var uri = new Uri($"https://management.azure.com/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/deployments/?api-version=2021-04-01");
|
||||||
return await GetAllByUri(uri);
|
return await GetAllByUri(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
using AzureRestApi.Models.Resources;
|
using AzureRestApi.Models.Resources;
|
||||||
|
|
||||||
namespace AzureRestApi.Repositories
|
namespace AzureRestApi.Repositories;
|
||||||
{
|
|
||||||
public class AzureResourceGroupRepository : AzureBaseRepository<ResourceGroup>
|
|
||||||
{
|
|
||||||
public AzureResourceGroupRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository) : base(httpClient, accessTokenRepository)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public class AzureResourceGroupRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository)
|
||||||
|
: AzureBaseRepository<ResourceGroup>(httpClient, accessTokenRepository)
|
||||||
|
{
|
||||||
public async Task<List<ResourceGroup>> GetAll(string subscriptionId)
|
public async Task<List<ResourceGroup>> GetAll(string subscriptionId)
|
||||||
{
|
{
|
||||||
// GET https://management.azure.com/subscriptions/{subscriptionId}/resourcegroups?api-version=2021-04-01
|
// GET https://management.azure.com/subscriptions/{subscriptionId}/resourcegroups?api-version=2021-04-01
|
||||||
@@ -15,4 +12,3 @@ namespace AzureRestApi.Repositories
|
|||||||
return await GetAllByUri(uri);
|
return await GetAllByUri(uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
using AzureRestApi.Models.Api;
|
using AzureRestApi.Models.Resources;
|
||||||
using AzureRestApi.Models.Resources;
|
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace AzureRestApi.Repositories
|
namespace AzureRestApi.Repositories;
|
||||||
{
|
|
||||||
public class AzureResourceRepository : AzureBaseRepository<Resource>
|
|
||||||
{
|
|
||||||
public AzureResourceRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository) : base(httpClient, accessTokenRepository)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public class AzureResourceRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository)
|
||||||
|
: AzureBaseRepository<Resource>(httpClient, accessTokenRepository)
|
||||||
|
{
|
||||||
public async Task<List<Resource>> GetAll(string subscriptionId)
|
public async Task<List<Resource>> GetAll(string subscriptionId)
|
||||||
{
|
{
|
||||||
// GET https://management.azure.com/subscriptions/{subscriptionId}/resources?$filter={$filter}&$expand={$expand}&$top={$top}&api-version=2021-04-01
|
// GET https://management.azure.com/subscriptions/{subscriptionId}/resources?$filter={$filter}&$expand={$expand}&$top={$top}&api-version=2021-04-01
|
||||||
@@ -18,4 +12,3 @@ namespace AzureRestApi.Repositories
|
|||||||
return await GetAllByUri(uri);
|
return await GetAllByUri(uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,24 +3,14 @@ using AzureRestApi.Models.Resources;
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace AzureRestApi.Repositories
|
namespace AzureRestApi.Repositories;
|
||||||
{
|
|
||||||
public class AzureSubscriptionRepository
|
|
||||||
{
|
|
||||||
private readonly HttpClient _httpClient;
|
|
||||||
private readonly AccessTokenRepository _accessTokenRepository;
|
|
||||||
|
|
||||||
public AzureSubscriptionRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository)
|
public class AzureSubscriptionRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
|
||||||
_accessTokenRepository = accessTokenRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<Subscription>?> GetAll()
|
public async Task<List<Subscription>?> GetAll()
|
||||||
{
|
{
|
||||||
// GET https://management.azure.com/subscriptions?api-version=2020-01-01
|
// GET https://management.azure.com/subscriptions?api-version=2020-01-01
|
||||||
|
var accessToken = await accessTokenRepository.GetAccessToken();
|
||||||
var accessToken = await _accessTokenRepository.GetAccessToken();
|
|
||||||
var uri = new Uri("https://management.azure.com/subscriptions?api-version=2020-01-01");
|
var uri = new Uri("https://management.azure.com/subscriptions?api-version=2020-01-01");
|
||||||
|
|
||||||
HttpRequestMessage request = new(HttpMethod.Get, uri);
|
HttpRequestMessage request = new(HttpMethod.Get, uri);
|
||||||
@@ -28,7 +18,7 @@ namespace AzureRestApi.Repositories
|
|||||||
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
|
||||||
|
|
||||||
var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
|
var response = await httpClient.SendAsync(request).ConfigureAwait(false);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
@@ -36,4 +26,3 @@ namespace AzureRestApi.Repositories
|
|||||||
return JsonSerializer.Deserialize< ApiResponse<Subscription>>(responseString)?.value;
|
return JsonSerializer.Deserialize< ApiResponse<Subscription>>(responseString)?.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,26 +4,15 @@ using System.Net.Http.Headers;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace AzureRestApi.Repositories
|
namespace AzureRestApi.Repositories;
|
||||||
{
|
|
||||||
public class AzureTagRepository
|
|
||||||
{
|
|
||||||
private readonly HttpClient _httpClient;
|
|
||||||
private readonly AccessTokenRepository _accessTokenRepository;
|
|
||||||
private readonly ILogger<AzureTagRepository> _logger;
|
|
||||||
|
|
||||||
public AzureTagRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository, ILogger<AzureTagRepository> logger)
|
public class AzureTagRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
|
||||||
_accessTokenRepository = accessTokenRepository;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> SetTag(string scope, string name, string value)
|
public async Task<bool> SetTag(string scope, string name, string value)
|
||||||
{
|
{
|
||||||
// PUT https://management.azure.com/{scope}/providers/Microsoft.Resources/tags/default?api-version=2021-04-01
|
// PUT https://management.azure.com/{scope}/providers/Microsoft.Resources/tags/default?api-version=2021-04-01
|
||||||
var result = true;
|
var result = true;
|
||||||
var accessToken = await _accessTokenRepository.GetAccessToken();
|
var accessToken = await accessTokenRepository.GetAccessToken();
|
||||||
var uri = new Uri("https://management.azure.com/" + scope + "/providers/Microsoft.Resources/tags/default?api-version=2021-04-01");
|
var uri = new Uri("https://management.azure.com/" + scope + "/providers/Microsoft.Resources/tags/default?api-version=2021-04-01");
|
||||||
|
|
||||||
var tagProperties = new TagProperties();
|
var tagProperties = new TagProperties();
|
||||||
@@ -36,7 +25,7 @@ namespace AzureRestApi.Repositories
|
|||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
|
||||||
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
|
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
|
var response = await httpClient.SendAsync(request).ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
@@ -50,4 +39,3 @@ namespace AzureRestApi.Repositories
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,27 +1,22 @@
|
|||||||
using AzureRestApi.Repositories;
|
using AzureRestApi.Repositories;
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
|
|
||||||
namespace AzureRestApi.Services
|
namespace AzureRestApi.Services;
|
||||||
{
|
|
||||||
public class CreatedOnDateService
|
public class CreatedOnDateService(
|
||||||
|
AzureSubscriptionRepository azureSubscriptionRepository,
|
||||||
|
AzureResourceRepository azureResourceRepository,
|
||||||
|
AzureTagRepository azureTagRepository)
|
||||||
{
|
{
|
||||||
private const string TagName = "CreatedOnDate";
|
private const string TagName = "CreatedOnDate";
|
||||||
private readonly AzureSubscriptionRepository _azureSubscriptionRepository;
|
|
||||||
private readonly AzureResourceRepository _azureResourceRepository;
|
|
||||||
private readonly AzureTagRepository _azureTagRepository;
|
|
||||||
|
|
||||||
public CreatedOnDateService(AzureSubscriptionRepository azureSubscriptionRepository, AzureResourceRepository azureResourceRepository, AzureTagRepository azureTagRepository)
|
|
||||||
{
|
|
||||||
_azureSubscriptionRepository = azureSubscriptionRepository;
|
|
||||||
_azureResourceRepository = azureResourceRepository;
|
|
||||||
_azureTagRepository = azureTagRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SetCreatedOnDateTags(bool skipDone = true)
|
public async Task SetCreatedOnDateTags(bool skipDone = true)
|
||||||
{
|
{
|
||||||
var rule = new Rule("[skyblue1]Setting CreatedOnDateTags[/]");
|
var rule = new Rule("[skyblue1]Setting CreatedOnDateTags[/]")
|
||||||
rule.Alignment = Justify.Left;
|
{
|
||||||
rule.Style = Style.Parse("skyblue1");
|
Justification = Justify.Left,
|
||||||
|
Style = Style.Parse("skyblue1")
|
||||||
|
};
|
||||||
AnsiConsole.Write(rule);
|
AnsiConsole.Write(rule);
|
||||||
|
|
||||||
await AnsiConsole.Status()
|
await AnsiConsole.Status()
|
||||||
@@ -29,22 +24,24 @@ namespace AzureRestApi.Services
|
|||||||
.Spinner(Spinner.Known.Default)
|
.Spinner(Spinner.Known.Default)
|
||||||
.StartAsync("Retrieving subscriptions...", async ctx =>
|
.StartAsync("Retrieving subscriptions...", async ctx =>
|
||||||
{
|
{
|
||||||
var subscriptions = await _azureSubscriptionRepository.GetAll();
|
var subscriptions = await azureSubscriptionRepository.GetAll();
|
||||||
if (subscriptions == null || !subscriptions.Any()) throw new Exception("No subscriptions found");
|
if (subscriptions == null || subscriptions.Count == 0) throw new Exception("No subscriptions found");
|
||||||
subscriptions = subscriptions.Where(s => s.Enabled && !string.IsNullOrWhiteSpace(s.subscriptionId)).ToList();
|
subscriptions = subscriptions.Where(s => s.Enabled && !string.IsNullOrWhiteSpace(s.subscriptionId)).ToList();
|
||||||
if (!subscriptions.Any()) throw new Exception("No subscriptions found");
|
if (subscriptions.Count == 0) throw new Exception("No subscriptions found");
|
||||||
|
|
||||||
foreach (var subscription in subscriptions)
|
foreach (var subscription in subscriptions)
|
||||||
{
|
{
|
||||||
rule = new Rule($"[skyblue1]{subscription.displayName}[/]");
|
rule = new Rule($"[skyblue1]{subscription.displayName}[/]")
|
||||||
rule.Alignment = Justify.Left;
|
{
|
||||||
rule.Style = Style.Parse("skyblue1 dim");
|
Justification = Justify.Left,
|
||||||
|
Style = Style.Parse("skyblue1 dim")
|
||||||
|
};
|
||||||
AnsiConsole.Write(rule);
|
AnsiConsole.Write(rule);
|
||||||
|
|
||||||
ctx.Status($"Getting the resources for subscription '{subscription.displayName}'");
|
ctx.Status($"Getting the resources for subscription '{subscription.displayName}'");
|
||||||
|
|
||||||
var resources = await _azureResourceRepository.GetAll(subscription.subscriptionId);
|
var resources = await azureResourceRepository.GetAll(subscription.subscriptionId);
|
||||||
if (resources == null || !resources.Any()) continue;
|
if (resources == null || resources.Count == 0) continue;
|
||||||
resources = resources.Where(r => !string.IsNullOrWhiteSpace(r.CreatedOn) && !string.IsNullOrWhiteSpace(r.id)).ToList();
|
resources = resources.Where(r => !string.IsNullOrWhiteSpace(r.CreatedOn) && !string.IsNullOrWhiteSpace(r.id)).ToList();
|
||||||
|
|
||||||
if (skipDone)
|
if (skipDone)
|
||||||
@@ -55,11 +52,10 @@ namespace AzureRestApi.Services
|
|||||||
ctx.Status($"Setting created date tag on resources in subscription '{subscription.displayName}'");
|
ctx.Status($"Setting created date tag on resources in subscription '{subscription.displayName}'");
|
||||||
foreach (var resource in resources)
|
foreach (var resource in resources)
|
||||||
{
|
{
|
||||||
var tagSuccess = await _azureTagRepository.SetTag(resource.id, TagName, resource.CreatedOn);
|
var tagSuccess = await azureTagRepository.SetTag(resource.id, TagName, resource.CreatedOn);
|
||||||
AnsiConsole.MarkupLine($"{resource.id} " + (tagSuccess ? "[green]V[/]" : "[red]X[/]"));
|
AnsiConsole.MarkupLine($"{resource.id} " + (tagSuccess ? "[green]V[/]" : "[red]X[/]"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,26 +1,20 @@
|
|||||||
using AzureRestApi.Repositories;
|
using AzureRestApi.Repositories;
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
|
|
||||||
namespace AzureRestApi.Services
|
namespace AzureRestApi.Services;
|
||||||
{
|
|
||||||
public class DeploymentTypeService
|
|
||||||
{
|
|
||||||
private readonly AzureSubscriptionRepository _azureSubscriptionRepository;
|
|
||||||
private readonly AzureDeploymentRepository _azureDeploymentRepository;
|
|
||||||
private readonly AzureResourceGroupRepository _azureResourceGroupRepository;
|
|
||||||
|
|
||||||
public DeploymentTypeService(AzureSubscriptionRepository azureSubscriptionRepository, AzureDeploymentRepository azureDeploymentRepository, AzureResourceGroupRepository azureResourceGroupRepository)
|
public class DeploymentTypeService(
|
||||||
|
AzureSubscriptionRepository azureSubscriptionRepository,
|
||||||
|
AzureDeploymentRepository azureDeploymentRepository,
|
||||||
|
AzureResourceGroupRepository azureResourceGroupRepository)
|
||||||
{
|
{
|
||||||
_azureSubscriptionRepository = azureSubscriptionRepository;
|
|
||||||
_azureDeploymentRepository = azureDeploymentRepository;
|
|
||||||
_azureResourceGroupRepository = azureResourceGroupRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task CheckDeploymentTypes()
|
public async Task CheckDeploymentTypes()
|
||||||
{
|
{
|
||||||
var rule = new Rule("[skyblue1]Checking deployment types[/]");
|
var rule = new Rule("[skyblue1]Checking deployment types[/]")
|
||||||
rule.Alignment = Justify.Left;
|
{
|
||||||
rule.Style = Style.Parse("skyblue1");
|
Justification = Justify.Left,
|
||||||
|
Style = Style.Parse("skyblue1")
|
||||||
|
};
|
||||||
AnsiConsole.Write(rule);
|
AnsiConsole.Write(rule);
|
||||||
|
|
||||||
await AnsiConsole.Status()
|
await AnsiConsole.Status()
|
||||||
@@ -29,22 +23,24 @@ namespace AzureRestApi.Services
|
|||||||
.StartAsync("Retrieving subscriptions...", async ctx =>
|
.StartAsync("Retrieving subscriptions...", async ctx =>
|
||||||
{
|
{
|
||||||
// Retrieve subscriptions
|
// Retrieve subscriptions
|
||||||
var subscriptions = await _azureSubscriptionRepository.GetAll();
|
var subscriptions = await azureSubscriptionRepository.GetAll();
|
||||||
if (subscriptions == null || !subscriptions.Any()) throw new Exception("No subscriptions found");
|
if (subscriptions == null || !subscriptions.Any()) throw new Exception("No subscriptions found");
|
||||||
subscriptions = subscriptions.Where(s => s.Enabled && !string.IsNullOrWhiteSpace(s.subscriptionId)).ToList();
|
subscriptions = subscriptions.Where(s => s.Enabled && !string.IsNullOrWhiteSpace(s.subscriptionId)).ToList();
|
||||||
if (!subscriptions.Any()) throw new Exception("No subscriptions found");
|
if (subscriptions.Count == 0) throw new Exception("No subscriptions found");
|
||||||
|
|
||||||
foreach (var subscription in subscriptions)
|
foreach (var subscription in subscriptions)
|
||||||
{
|
{
|
||||||
rule = new Rule($"[skyblue1]{subscription.displayName}[/]");
|
rule = new Rule($"[skyblue1]{subscription.displayName}[/]")
|
||||||
rule.Alignment = Justify.Left;
|
{
|
||||||
rule.Style = Style.Parse("skyblue1 dim");
|
Justification = Justify.Left,
|
||||||
|
Style = Style.Parse("skyblue1 dim")
|
||||||
|
};
|
||||||
AnsiConsole.Write(rule);
|
AnsiConsole.Write(rule);
|
||||||
|
|
||||||
ctx.Status($"Getting the deployments for subscription '{subscription.displayName}'");
|
ctx.Status($"Getting the deployments for subscription '{subscription.displayName}'");
|
||||||
var deployments = await _azureDeploymentRepository.GetAllBySubscription(subscription.subscriptionId);
|
var deployments = await azureDeploymentRepository.GetAllBySubscription(subscription.subscriptionId);
|
||||||
|
|
||||||
if (deployments == null || !deployments.Any()) continue;
|
if (deployments == null || deployments.Count == 0) continue;
|
||||||
|
|
||||||
foreach (var deployment in deployments)
|
foreach (var deployment in deployments)
|
||||||
{
|
{
|
||||||
@@ -52,22 +48,18 @@ namespace AzureRestApi.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.Status($"Getting the resource groups for subscription '{subscription.displayName}'");
|
ctx.Status($"Getting the resource groups for subscription '{subscription.displayName}'");
|
||||||
var resourceGroups = await _azureResourceGroupRepository.GetAll(subscription.subscriptionId);
|
var resourceGroups = await azureResourceGroupRepository.GetAll(subscription.subscriptionId);
|
||||||
foreach (var resourceGroup in resourceGroups)
|
foreach (var resourceGroup in resourceGroups)
|
||||||
{
|
{
|
||||||
ctx.Status($"Getting the deployments for resourcegroup {resourceGroup.name} in subscription '{subscription.displayName}'");
|
ctx.Status($"Getting the deployments for resourcegroup {resourceGroup.name} in subscription '{subscription.displayName}'");
|
||||||
|
|
||||||
var resourceGroupDeployments = await _azureDeploymentRepository.GetAllByResourceGroup(subscription.subscriptionId, resourceGroup.name);
|
var resourceGroupDeployments = await azureDeploymentRepository.GetAllByResourceGroup(subscription.subscriptionId, resourceGroup.name);
|
||||||
foreach (var deployment in resourceGroupDeployments)
|
foreach (var deployment in resourceGroupDeployments)
|
||||||
{
|
{
|
||||||
AnsiConsole.WriteLine(Markup.Escape($"{resourceGroup.name} - {deployment?.name}"));
|
AnsiConsole.WriteLine(Markup.Escape($"{resourceGroup.name} - {deployment?.name}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,27 +1,11 @@
|
|||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace AzureRestApi.Services
|
namespace AzureRestApi.Services;
|
||||||
{
|
|
||||||
public class OptionService : IHostedService
|
|
||||||
{
|
|
||||||
private readonly ILogger<OptionService> _logger;
|
|
||||||
private readonly CreatedOnDateService _createdOnDateService;
|
|
||||||
private readonly DeploymentTypeService _deploymentTypeService;
|
|
||||||
|
|
||||||
public OptionService(ILogger<OptionService> logger, CreatedOnDateService createdOnDateService, DeploymentTypeService deploymentTypeService)
|
public class OptionService(CreatedOnDateService createdOnDateService, DeploymentTypeService deploymentTypeService)
|
||||||
|
: IHostedService
|
||||||
{
|
{
|
||||||
_logger = logger;
|
|
||||||
_createdOnDateService = createdOnDateService;
|
|
||||||
_deploymentTypeService = deploymentTypeService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task StartAsync(CancellationToken cancellationToken)
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var rule = new Rule("[yellow]Cloud Egineering Console App[/]");
|
var rule = new Rule("[yellow]Cloud Egineering Console App[/]");
|
||||||
@@ -44,11 +28,11 @@ namespace AzureRestApi.Services
|
|||||||
|
|
||||||
if (result == choices[0])
|
if (result == choices[0])
|
||||||
{
|
{
|
||||||
await _createdOnDateService.SetCreatedOnDateTags(true);
|
await createdOnDateService.SetCreatedOnDateTags(true);
|
||||||
}
|
}
|
||||||
else if (result == choices[1])
|
else if (result == choices[1])
|
||||||
{
|
{
|
||||||
await _deploymentTypeService.CheckDeploymentTypes();
|
await deploymentTypeService.CheckDeploymentTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
rule = new Rule("[yellow]Done. Bye.[/]");
|
rule = new Rule("[yellow]Done. Bye.[/]");
|
||||||
@@ -61,4 +45,3 @@ namespace AzureRestApi.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ConsoleApp1
|
|
||||||
{
|
|
||||||
internal class Program
|
|
||||||
{
|
|
||||||
static void Main(string[] args)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Hello World!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace SnykRestApi.Models.Parsed
|
namespace SnykRestApi.Models.Parsed;
|
||||||
{
|
|
||||||
public class AuditLog
|
public class AuditLog
|
||||||
{
|
{
|
||||||
public string GroupId { get; set; }
|
public string GroupId { get; set; }
|
||||||
@@ -12,4 +12,3 @@
|
|||||||
public string Event { get; set; }
|
public string Event { get; set; }
|
||||||
public DateTime Created { get; set; }
|
public DateTime Created { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace SnykRestApi.Models.Raw
|
namespace SnykRestApi.Models.Raw;
|
||||||
{
|
|
||||||
internal class AuditLogResponse
|
internal class AuditLogResponse
|
||||||
{
|
{
|
||||||
[JsonPropertyName("groupId")]
|
[JsonPropertyName("groupId")]
|
||||||
@@ -21,4 +21,3 @@ namespace SnykRestApi.Models.Raw
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace SnykRestApi.Models.Raw
|
namespace SnykRestApi.Models.Raw;
|
||||||
{
|
|
||||||
internal class GroupResponse
|
internal class GroupResponse
|
||||||
{
|
{
|
||||||
[JsonPropertyName("name")]
|
[JsonPropertyName("name")]
|
||||||
@@ -10,4 +10,3 @@ namespace SnykRestApi.Models.Raw
|
|||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace SnykRestApi.Models.Raw
|
namespace SnykRestApi.Models.Raw;
|
||||||
{
|
|
||||||
internal class OrganizationListResponse
|
internal class OrganizationListResponse
|
||||||
{
|
{
|
||||||
[JsonPropertyName("orgs")]
|
[JsonPropertyName("orgs")]
|
||||||
public List<OrganizationResponse> Orgs { get; set; }
|
public List<OrganizationResponse> Orgs { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace SnykRestApi.Models.Raw
|
namespace SnykRestApi.Models.Raw;
|
||||||
{
|
|
||||||
internal class OrganizationResponse
|
internal class OrganizationResponse
|
||||||
{
|
{
|
||||||
[JsonPropertyName("name")]
|
[JsonPropertyName("name")]
|
||||||
@@ -16,4 +16,3 @@ namespace SnykRestApi.Models.Raw
|
|||||||
public GroupResponse Group { get; set; }
|
public GroupResponse Group { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace SnykRestApi.Models.Raw
|
namespace SnykRestApi.Models.Raw;
|
||||||
{
|
|
||||||
internal class ProjectListResponse
|
internal class ProjectListResponse
|
||||||
{
|
{
|
||||||
[JsonPropertyName("org")]
|
[JsonPropertyName("org")]
|
||||||
@@ -9,4 +9,3 @@ namespace SnykRestApi.Models.Raw
|
|||||||
[JsonPropertyName("projects")]
|
[JsonPropertyName("projects")]
|
||||||
public List<ProjectResponse> Projects { get; set; }
|
public List<ProjectResponse> Projects { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace SnykRestApi.Models.Raw
|
namespace SnykRestApi.Models.Raw;
|
||||||
{
|
|
||||||
internal class ProjectResponse
|
internal class ProjectResponse
|
||||||
{
|
{
|
||||||
[JsonPropertyName("name")]
|
[JsonPropertyName("name")]
|
||||||
@@ -9,4 +9,3 @@ namespace SnykRestApi.Models.Raw
|
|||||||
[JsonPropertyName("id")]
|
[JsonPropertyName("id")]
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace SnykRestApi.Models.Raw
|
namespace SnykRestApi.Models.Raw;
|
||||||
{
|
|
||||||
internal class UserResponse
|
internal class UserResponse
|
||||||
{
|
{
|
||||||
[JsonPropertyName("name")]
|
[JsonPropertyName("name")]
|
||||||
@@ -14,4 +14,3 @@ namespace SnykRestApi.Models.Raw
|
|||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace SnykRestApi.Models
|
namespace SnykRestApi.Models;
|
||||||
{
|
|
||||||
public class Settings
|
public class Settings
|
||||||
{
|
{
|
||||||
public string KeyVaultName { get; set; } = string.Empty;
|
public string KeyVaultName { get; set; } = string.Empty;
|
||||||
@@ -8,4 +8,3 @@
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
namespace SnykRestApi
|
namespace SnykRestApi;
|
||||||
{
|
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
static Task Main(string[] args) =>
|
static Task Main(string[] args) =>
|
||||||
@@ -37,4 +37,3 @@ namespace SnykRestApi
|
|||||||
services.AddHostedService<OptionService>();
|
services.AddHostedService<OptionService>();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -2,23 +2,17 @@
|
|||||||
using Azure.Security.KeyVault.Secrets;
|
using Azure.Security.KeyVault.Secrets;
|
||||||
using SnykRestApi.Models;
|
using SnykRestApi.Models;
|
||||||
|
|
||||||
namespace SnykRestApi.Repositories
|
namespace SnykRestApi.Repositories;
|
||||||
{
|
|
||||||
public class AccessTokenRepository
|
|
||||||
{
|
|
||||||
private readonly Settings _settings;
|
|
||||||
private string? _authorizationToken = string.Empty;
|
|
||||||
|
|
||||||
public AccessTokenRepository(Settings settings)
|
public class AccessTokenRepository(Settings settings)
|
||||||
{
|
{
|
||||||
_settings = settings;
|
private string? _authorizationToken = string.Empty;
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetAuthorizationToken()
|
public async Task<string> GetAuthorizationToken()
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(_authorizationToken)) return _authorizationToken;
|
if (!string.IsNullOrWhiteSpace(_authorizationToken)) return _authorizationToken;
|
||||||
|
|
||||||
var keyvaultUri = "https://" + _settings.KeyVaultName + ".vault.azure.net";
|
var keyvaultUri = "https://" + settings.KeyVaultName + ".vault.azure.net";
|
||||||
var credential = new DefaultAzureCredential();
|
var credential = new DefaultAzureCredential();
|
||||||
var client = new SecretClient(new Uri(keyvaultUri), credential);
|
var client = new SecretClient(new Uri(keyvaultUri), credential);
|
||||||
_authorizationToken = (await client.GetSecretAsync("SnykKey")).Value.Value;
|
_authorizationToken = (await client.GetSecretAsync("SnykKey")).Value.Value;
|
||||||
@@ -26,4 +20,3 @@ namespace SnykRestApi.Repositories
|
|||||||
return _authorizationToken;
|
return _authorizationToken;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,37 +3,26 @@ using SnykRestApi.Models.Raw;
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace SnykRestApi.Repositories
|
namespace SnykRestApi.Repositories;
|
||||||
{
|
|
||||||
public class AuditLogRepository
|
|
||||||
{
|
|
||||||
private readonly HttpClient _httpClient;
|
|
||||||
private readonly AccessTokenRepository _accessTokenRepository;
|
|
||||||
private readonly Settings _settings;
|
|
||||||
|
|
||||||
public AuditLogRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository, Settings settings)
|
public class AuditLogRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository, Settings settings)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
internal async Task<List<AuditLogResponse>> GetByOrganizationId(string organizationId)
|
||||||
_accessTokenRepository = accessTokenRepository;
|
|
||||||
_settings = settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task<List<AuditLogResponse>> GetByOrganizationId(string origanizationId)
|
|
||||||
{
|
{
|
||||||
var authorizationToken = await _accessTokenRepository.GetAuthorizationToken();
|
var authorizationToken = await accessTokenRepository.GetAuthorizationToken();
|
||||||
List<AuditLogResponse> result = new();
|
List<AuditLogResponse> result = [];
|
||||||
List<AuditLogResponse> responseItems;
|
List<AuditLogResponse> responseItems;
|
||||||
|
|
||||||
int page = 0;
|
var page = 0;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
HttpRequestMessage request = new(HttpMethod.Post, $"{_settings.SnykBaseUrl}org/{origanizationId}/audit?from=2022-07-01&page={++page}");
|
HttpRequestMessage request = new(HttpMethod.Post, $"{settings.SnykBaseUrl}org/{organizationId}/audit?from=2022-07-01&page={++page}");
|
||||||
request.Headers.Accept.Clear();
|
request.Headers.Accept.Clear();
|
||||||
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("token", authorizationToken);
|
request.Headers.Authorization = new AuthenticationHeaderValue("token", authorizationToken);
|
||||||
|
|
||||||
var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
|
var response = await httpClient.SendAsync(request).ConfigureAwait(false);
|
||||||
|
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
@@ -46,4 +35,3 @@ namespace SnykRestApi.Repositories
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,30 +3,17 @@ using SnykRestApi.Models;
|
|||||||
using SnykRestApi.Models.Parsed;
|
using SnykRestApi.Models.Parsed;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
namespace SnykRestApi.Repositories
|
namespace SnykRestApi.Repositories;
|
||||||
{
|
|
||||||
public class CsvRepository
|
|
||||||
{
|
|
||||||
private readonly Settings _settings;
|
|
||||||
|
|
||||||
public CsvRepository(Settings settings)
|
public class CsvRepository(Settings settings)
|
||||||
{
|
{
|
||||||
_settings = settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task WriteAll (List<AuditLog> log)
|
public async Task WriteAll (List<AuditLog> log)
|
||||||
{
|
{
|
||||||
var t = DateTime.Now;
|
var t = DateTime.Now;
|
||||||
var fileName = $"{_settings.CsvFolder}SnykAuditLog_{t:yyyy}{t:MM}{t:dd}_{t:HH}{t:mm}{t:ss}_{t:FFF}.csv";
|
var fileName = $"{settings.CsvFolder}SnykAuditLog_{t:yyyy}{t:MM}{t:dd}_{t:HH}{t:mm}{t:ss}_{t:FFF}.csv";
|
||||||
|
|
||||||
using (var writer = new StreamWriter(fileName))
|
await using var writer = new StreamWriter(fileName);
|
||||||
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
|
await using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
|
||||||
{
|
|
||||||
await csv.WriteRecordsAsync(log);
|
await csv.WriteRecordsAsync(log);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -3,38 +3,26 @@ using SnykRestApi.Models.Raw;
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace SnykRestApi.Repositories
|
namespace SnykRestApi.Repositories;
|
||||||
{
|
|
||||||
public class OrganizationRepository
|
|
||||||
{
|
|
||||||
private readonly HttpClient _httpClient;
|
|
||||||
private readonly AccessTokenRepository _accessTokenRepository;
|
|
||||||
private readonly Settings _settings;
|
|
||||||
|
|
||||||
public OrganizationRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository, Settings settings)
|
public class OrganizationRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository, Settings settings)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
|
||||||
_accessTokenRepository = accessTokenRepository;
|
|
||||||
_settings = settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task<List<OrganizationResponse>> GetAll()
|
internal async Task<List<OrganizationResponse>> GetAll()
|
||||||
{
|
{
|
||||||
var authorizationToken = await _accessTokenRepository.GetAuthorizationToken();
|
var authorizationToken = await accessTokenRepository.GetAuthorizationToken();
|
||||||
|
|
||||||
var uri = $"{_settings.SnykBaseUrl}orgs";
|
var uri = $"{settings.SnykBaseUrl}orgs";
|
||||||
HttpRequestMessage request = new(HttpMethod.Get, uri);
|
HttpRequestMessage request = new(HttpMethod.Get, uri);
|
||||||
request.Headers.Accept.Clear();
|
request.Headers.Accept.Clear();
|
||||||
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("token", authorizationToken);
|
request.Headers.Authorization = new AuthenticationHeaderValue("token", authorizationToken);
|
||||||
|
|
||||||
var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
|
var response = await httpClient.SendAsync(request).ConfigureAwait(false);
|
||||||
|
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
var result = JsonSerializer.Deserialize<OrganizationListResponse>(responseString)?.Orgs;
|
var result = JsonSerializer.Deserialize<OrganizationListResponse>(responseString)?.Orgs;
|
||||||
return result ?? new List<OrganizationResponse>();
|
return result ?? [];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,38 +3,26 @@ using SnykRestApi.Models.Raw;
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace SnykRestApi.Repositories
|
namespace SnykRestApi.Repositories;
|
||||||
{
|
|
||||||
public class ProjectRepository
|
|
||||||
{
|
|
||||||
private readonly HttpClient _httpClient;
|
|
||||||
private readonly AccessTokenRepository _accessTokenRepository;
|
|
||||||
private readonly Settings _settings;
|
|
||||||
|
|
||||||
public ProjectRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository, Settings settings)
|
public class ProjectRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository, Settings settings)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
|
||||||
_accessTokenRepository = accessTokenRepository;
|
|
||||||
_settings = settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task<List<ProjectResponse>> GetAll(string organizationId)
|
internal async Task<List<ProjectResponse>> GetAll(string organizationId)
|
||||||
{
|
{
|
||||||
var authorizationToken = await _accessTokenRepository.GetAuthorizationToken();
|
var authorizationToken = await accessTokenRepository.GetAuthorizationToken();
|
||||||
|
|
||||||
var uri = $"{_settings.SnykBaseUrl}org/{organizationId}/projects";
|
var uri = $"{settings.SnykBaseUrl}org/{organizationId}/projects";
|
||||||
HttpRequestMessage request = new(HttpMethod.Post, uri);
|
HttpRequestMessage request = new(HttpMethod.Post, uri);
|
||||||
request.Headers.Accept.Clear();
|
request.Headers.Accept.Clear();
|
||||||
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("token", authorizationToken);
|
request.Headers.Authorization = new AuthenticationHeaderValue("token", authorizationToken);
|
||||||
|
|
||||||
var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
|
var response = await httpClient.SendAsync(request).ConfigureAwait(false);
|
||||||
|
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
var result = JsonSerializer.Deserialize<ProjectListResponse>(responseString)?.Projects;
|
var result = JsonSerializer.Deserialize<ProjectListResponse>(responseString)?.Projects;
|
||||||
return result ?? new List<ProjectResponse>();
|
return result ?? [];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,42 +4,30 @@ using Spectre.Console;
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace SnykRestApi.Repositories
|
namespace SnykRestApi.Repositories;
|
||||||
{
|
|
||||||
public class UserRepository
|
|
||||||
{
|
|
||||||
private readonly HttpClient _httpClient;
|
|
||||||
private readonly AccessTokenRepository _accessTokenRepository;
|
|
||||||
private readonly Settings _settings;
|
|
||||||
|
|
||||||
public UserRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository, Settings settings)
|
public class UserRepository(HttpClient httpClient, AccessTokenRepository accessTokenRepository, Settings settings)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
|
||||||
_accessTokenRepository = accessTokenRepository;
|
|
||||||
_settings = settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task<List<UserResponse>> GetAll(List<string> ids)
|
internal async Task<List<UserResponse>> GetAll(List<string> ids)
|
||||||
{
|
{
|
||||||
var authorizationToken = await _accessTokenRepository.GetAuthorizationToken();
|
var authorizationToken = await accessTokenRepository.GetAuthorizationToken();
|
||||||
List<UserResponse> result = new();
|
List<UserResponse> result = new();
|
||||||
UserResponse? responseItem;
|
|
||||||
|
|
||||||
foreach (var id in ids)
|
foreach (var id in ids)
|
||||||
{
|
{
|
||||||
HttpRequestMessage request = new(HttpMethod.Get, $"{_settings.SnykBaseUrl}user/{id}");
|
HttpRequestMessage request = new(HttpMethod.Get, $"{settings.SnykBaseUrl}user/{id}");
|
||||||
request.Headers.Accept.Clear();
|
request.Headers.Accept.Clear();
|
||||||
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("token", authorizationToken);
|
request.Headers.Authorization = new AuthenticationHeaderValue("token", authorizationToken);
|
||||||
|
|
||||||
var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
|
var response = await httpClient.SendAsync(request).ConfigureAwait(false);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
responseItem = JsonSerializer.Deserialize<UserResponse>(responseString);
|
var responseItem = JsonSerializer.Deserialize<UserResponse>(responseString);
|
||||||
|
|
||||||
if (responseItem != null)
|
if (responseItem != null)
|
||||||
{
|
{
|
||||||
@@ -62,4 +50,3 @@ namespace SnykRestApi.Repositories
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,35 +2,20 @@
|
|||||||
using SnykRestApi.Models.Raw;
|
using SnykRestApi.Models.Raw;
|
||||||
using SnykRestApi.Repositories;
|
using SnykRestApi.Repositories;
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace SnykRestApi.Services
|
namespace SnykRestApi.Services;
|
||||||
{
|
|
||||||
public class AuditLogService
|
|
||||||
{
|
|
||||||
private readonly OrganizationRepository _organizationRepository;
|
|
||||||
private readonly AuditLogRepository _auditLogRepository;
|
|
||||||
private readonly ProjectRepository _projectRepository;
|
|
||||||
private readonly UserRepository _userRepository;
|
|
||||||
private readonly CsvRepository _csvRepository;
|
|
||||||
|
|
||||||
public AuditLogService(OrganizationRepository organizationRepository, AuditLogRepository auditLogRepostitory, ProjectRepository projectRepository, UserRepository userRepository, CsvRepository csvRepository)
|
public class AuditLogService(
|
||||||
|
OrganizationRepository organizationRepository,
|
||||||
|
AuditLogRepository auditLogRepository,
|
||||||
|
ProjectRepository projectRepository,
|
||||||
|
UserRepository userRepository,
|
||||||
|
CsvRepository csvRepository)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
|
||||||
_auditLogRepository = auditLogRepostitory;
|
|
||||||
_projectRepository = projectRepository;
|
|
||||||
_userRepository = userRepository;
|
|
||||||
_csvRepository = csvRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task CreateAuditLog ()
|
public async Task CreateAuditLog ()
|
||||||
{
|
{
|
||||||
var rule = new Rule("[skyblue1]Creating Snyk Audit Log CSV[/]");
|
var rule = new Rule("[skyblue1]Creating Snyk Audit Log CSV[/]");
|
||||||
rule.Alignment = Justify.Left;
|
rule.Justification = Justify.Left;
|
||||||
rule.Style = Style.Parse("skyblue1");
|
rule.Style = Style.Parse("skyblue1");
|
||||||
AnsiConsole.Write(rule);
|
AnsiConsole.Write(rule);
|
||||||
|
|
||||||
@@ -39,7 +24,7 @@ namespace SnykRestApi.Services
|
|||||||
.Spinner(Spinner.Known.Default)
|
.Spinner(Spinner.Known.Default)
|
||||||
.StartAsync("Retrieving organizations...", async ctx =>
|
.StartAsync("Retrieving organizations...", async ctx =>
|
||||||
{
|
{
|
||||||
var organizations = await _organizationRepository.GetAll();
|
var organizations = await organizationRepository.GetAll();
|
||||||
if (organizations == null || !organizations.Any()) throw new Exception("No organizations found");
|
if (organizations == null || !organizations.Any()) throw new Exception("No organizations found");
|
||||||
|
|
||||||
var log = new List<AuditLogResponse>();
|
var log = new List<AuditLogResponse>();
|
||||||
@@ -48,35 +33,39 @@ namespace SnykRestApi.Services
|
|||||||
foreach (var organization in organizations)
|
foreach (var organization in organizations)
|
||||||
{
|
{
|
||||||
rule = new Rule($"[skyblue1]{organization.Name}[/]");
|
rule = new Rule($"[skyblue1]{organization.Name}[/]");
|
||||||
rule.Alignment = Justify.Left;
|
rule.Justification = Justify.Left;
|
||||||
rule.Style = Style.Parse("skyblue1 dim");
|
rule.Style = Style.Parse("skyblue1 dim");
|
||||||
AnsiConsole.Write(rule);
|
AnsiConsole.Write(rule);
|
||||||
|
|
||||||
ctx.Status($"Getting the projects for organization '{organization.Name}'");
|
ctx.Status($"Getting the projects for organization '{organization.Name}'");
|
||||||
var orgProjects = await _projectRepository.GetAll(organization.Id);
|
var orgProjects = await projectRepository.GetAll(organization.Id);
|
||||||
projects.AddRange(orgProjects);
|
projects.AddRange(orgProjects);
|
||||||
AnsiConsole.WriteLine($"Got {orgProjects.Count} projects.");
|
AnsiConsole.WriteLine($"Got {orgProjects.Count} projects.");
|
||||||
|
|
||||||
ctx.Status($"Getting the audit log for organization '{organization.Name}'");
|
ctx.Status($"Getting the audit log for organization '{organization.Name}'");
|
||||||
var orgLogs = await _auditLogRepository.GetByOrganizationId(organization.Id);
|
var orgLogs = await auditLogRepository.GetByOrganizationId(organization.Id);
|
||||||
log.AddRange(orgLogs);
|
log.AddRange(orgLogs);
|
||||||
AnsiConsole.WriteLine($"Got {orgLogs.Count} log records.");
|
AnsiConsole.WriteLine($"Got {orgLogs.Count} log records.");
|
||||||
}
|
}
|
||||||
|
|
||||||
rule = new Rule($"[skyblue1]Retrieving users[/]");
|
rule = new Rule("[skyblue1]Retrieving users[/]")
|
||||||
rule.Alignment = Justify.Left;
|
{
|
||||||
rule.Style = Style.Parse("skyblue1 dim");
|
Justification = Justify.Left,
|
||||||
|
Style = Style.Parse("skyblue1 dim")
|
||||||
|
};
|
||||||
AnsiConsole.Write(rule);
|
AnsiConsole.Write(rule);
|
||||||
|
|
||||||
ctx.Status($"Getting users");
|
ctx.Status($"Getting users");
|
||||||
var userIds = log.Select(l => l.UserId).Distinct().ToList();
|
var userIds = log.Select(l => l.UserId).Distinct().ToList();
|
||||||
|
|
||||||
var users = await _userRepository.GetAll(userIds);
|
var users = await userRepository.GetAll(userIds);
|
||||||
AnsiConsole.WriteLine($"Got {users.Count} users of {userIds.Count} user ids.");
|
AnsiConsole.WriteLine($"Got {users.Count} users of {userIds.Count} user ids.");
|
||||||
|
|
||||||
rule = new Rule($"[skyblue1]Creating CSV[/]");
|
rule = new Rule("[skyblue1]Creating CSV[/]")
|
||||||
rule.Alignment = Justify.Left;
|
{
|
||||||
rule.Style = Style.Parse("skyblue1 dim");
|
Justification = Justify.Left,
|
||||||
|
Style = Style.Parse("skyblue1 dim")
|
||||||
|
};
|
||||||
AnsiConsole.Write(rule);
|
AnsiConsole.Write(rule);
|
||||||
|
|
||||||
ctx.Status($"Combining all information");
|
ctx.Status($"Combining all information");
|
||||||
@@ -87,7 +76,7 @@ namespace SnykRestApi.Services
|
|||||||
from subU in gjU.DefaultIfEmpty()
|
from subU in gjU.DefaultIfEmpty()
|
||||||
join p in projects on l.ProjectId equals p.Id into gjP
|
join p in projects on l.ProjectId equals p.Id into gjP
|
||||||
from subP in gjP.DefaultIfEmpty()
|
from subP in gjP.DefaultIfEmpty()
|
||||||
select new AuditLog()
|
select new AuditLog
|
||||||
{
|
{
|
||||||
GroupId = l.GroupId,
|
GroupId = l.GroupId,
|
||||||
OrganizationId = l.OrgId,
|
OrganizationId = l.OrgId,
|
||||||
@@ -101,9 +90,8 @@ namespace SnykRestApi.Services
|
|||||||
}).ToList();
|
}).ToList();
|
||||||
AnsiConsole.WriteLine($"Prepared {result.Count} lines to export of {log.Count} audit log records.");
|
AnsiConsole.WriteLine($"Prepared {result.Count} lines to export of {log.Count} audit log records.");
|
||||||
|
|
||||||
ctx.Status($"Writing CSV");
|
ctx.Status("Writing CSV");
|
||||||
await _csvRepository.WriteAll(result);
|
await csvRepository.WriteAll(result);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,18 +2,11 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
|
|
||||||
namespace SnykRestApi.Services
|
namespace SnykRestApi.Services;
|
||||||
{
|
|
||||||
public class OptionService : IHostedService
|
|
||||||
{
|
|
||||||
private readonly ILogger<OptionService> _logger;
|
|
||||||
private readonly AuditLogService _auditLogService;
|
|
||||||
|
|
||||||
public OptionService(ILogger<OptionService> logger, AuditLogService auditLogService)
|
public class OptionService(AuditLogService auditLogService)
|
||||||
|
: IHostedService
|
||||||
{
|
{
|
||||||
_logger = logger;
|
|
||||||
_auditLogService = auditLogService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task StartAsync(CancellationToken cancellationToken)
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -37,7 +30,7 @@ namespace SnykRestApi.Services
|
|||||||
|
|
||||||
if (result == choices[0])
|
if (result == choices[0])
|
||||||
{
|
{
|
||||||
await _auditLogService.CreateAuditLog();
|
await auditLogService.CreateAuditLog();
|
||||||
}
|
}
|
||||||
//else if (result == choices[1])
|
//else if (result == choices[1])
|
||||||
//{
|
//{
|
||||||
@@ -53,4 +46,3 @@ namespace SnykRestApi.Services
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,21 +2,21 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Azure.Identity" Version="1.6.1" />
|
<PackageReference Include="Azure.Identity" Version="1.17.0" />
|
||||||
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.3.0" />
|
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.8.0" />
|
||||||
<PackageReference Include="CsvHelper" Version="28.0.1" />
|
<PackageReference Include="CsvHelper" Version="33.1.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
|
||||||
<PackageReference Include="Spectre.Console" Version="0.44.0" />
|
<PackageReference Include="Spectre.Console" Version="0.53.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
16
ConsoleApps/SonarClient/SonarClient.sln
Normal file
16
ConsoleApps/SonarClient/SonarClient.sln
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SonarClient", "SonarClient\SonarClient.csproj", "{1DF21902-A886-4C02-AB59-B19CB291498D}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{1DF21902-A886-4C02-AB59-B19CB291498D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1DF21902-A886-4C02-AB59-B19CB291498D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1DF21902-A886-4C02-AB59-B19CB291498D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1DF21902-A886-4C02-AB59-B19CB291498D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
using SonarClient.Repositories.Interfaces;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
namespace SonarClient.Actions;
|
||||||
|
|
||||||
|
public class UpdatePermissionsAndTags(
|
||||||
|
IProjectRepository projectRepository,
|
||||||
|
IPermissionRepository permissionRepository,
|
||||||
|
IProjectTagRepository projectTagRepository)
|
||||||
|
{
|
||||||
|
private Dictionary<string, string> PermissionTemplateMapping { get; set; } = new();
|
||||||
|
|
||||||
|
public async Task Execute(string organizationId, string organizationKey)
|
||||||
|
{
|
||||||
|
var projects = await projectRepository.GetProjects(organizationId);
|
||||||
|
var permissionTemplates = await permissionRepository.GetPermissionTemplates(organizationKey);
|
||||||
|
PermissionTemplateMapping = permissionTemplates.ToDictionary(x => x.Name, x => x.Id);
|
||||||
|
|
||||||
|
var projectTeams = new List<ProjectTeam>();
|
||||||
|
|
||||||
|
foreach (var project in projects)
|
||||||
|
{
|
||||||
|
if (project.Tags.Length == 0)
|
||||||
|
{
|
||||||
|
AnsiConsole.MarkupLine($"[red]Project {project.Name} has zero tags[/]");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldTeam = project.Tags[0];
|
||||||
|
|
||||||
|
var newTeam = GetNewTeam(oldTeam);
|
||||||
|
|
||||||
|
if (newTeam == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
projectTeams.Add(new ProjectTeam(project.Key, oldTeam, newTeam));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var teamProjects in projectTeams.GroupBy(p => p.OldTeam).ToDictionary(g => g.Key, g => g.ToList()))
|
||||||
|
{
|
||||||
|
AnsiConsole.MarkupLine($"Do you want to update permissions and tags for team: [yellow]{teamProjects.Key}[/] to [yellow]{GetNewTeam(teamProjects.Key)}[/]? It will be updated for the following projects:");
|
||||||
|
foreach (var project in teamProjects.Value)
|
||||||
|
{
|
||||||
|
AnsiConsole.MarkupLine($"[green]{Markup.Escape($"- {project.ProjectKey}")}[/]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await AnsiConsole.ConfirmAsync("Confirm"))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var project in teamProjects.Value)
|
||||||
|
{
|
||||||
|
await permissionRepository.ApplyTemplateToProject(project.ProjectKey, GetPermissionTemplateName(project.NewTeam));
|
||||||
|
await projectTagRepository.SetTags(project.ProjectKey, [project.NewTeam]);
|
||||||
|
AnsiConsole.MarkupLine($"[green]Updated permissions and tags for project: [yellow]{project.ProjectKey}[/][/]");
|
||||||
|
}
|
||||||
|
AnsiConsole.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetNewTeam(string oldTeam)
|
||||||
|
{
|
||||||
|
return oldTeam switch
|
||||||
|
{
|
||||||
|
"lime" => "platform",
|
||||||
|
"yellow" => "reporting",
|
||||||
|
"orange" => "reporting",
|
||||||
|
"red" => "surveying",
|
||||||
|
"pink" => "surveying",
|
||||||
|
"gray" => "surveying",
|
||||||
|
"blue" => "data-and-ai",
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetPermissionTemplateName(string newTeam)
|
||||||
|
{
|
||||||
|
return newTeam switch
|
||||||
|
{
|
||||||
|
"platform" => PermissionTemplateMapping["Team Platform template"],
|
||||||
|
"reporting" => PermissionTemplateMapping["Team Reporting template"],
|
||||||
|
"surveying" => PermissionTemplateMapping["Team Surveying template"],
|
||||||
|
"data-and-ai" => PermissionTemplateMapping["Team Data and AI template"],
|
||||||
|
"managers" => PermissionTemplateMapping["Team Managers template"],
|
||||||
|
_ => throw new IndexOutOfRangeException($"Unknown team: {newTeam}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private record ProjectTeam(string ProjectKey, string OldTeam, string NewTeam);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace SonarClient.Constants;
|
||||||
|
|
||||||
|
public static class SonarConstants
|
||||||
|
{
|
||||||
|
public const string SonarApiV1ClientName = "SonarApiV1Client";
|
||||||
|
public const string SonarApiV1BaseAddress = "https://sonarcloud.io/";
|
||||||
|
|
||||||
|
public const string SonarApiV2ClientName = "SonarApiV2Client";
|
||||||
|
public const string SonarApiV2BaseAddress = "https://api.sonarcloud.io/";
|
||||||
|
}
|
||||||
8
ConsoleApps/SonarClient/SonarClient/Models/Link.cs
Normal file
8
ConsoleApps/SonarClient/SonarClient/Models/Link.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace SonarClient.Models;
|
||||||
|
|
||||||
|
public class Link
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Url { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
}
|
||||||
9
ConsoleApps/SonarClient/SonarClient/Models/Paging.cs
Normal file
9
ConsoleApps/SonarClient/SonarClient/Models/Paging.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace SonarClient.Models;
|
||||||
|
|
||||||
|
public class Paging
|
||||||
|
{
|
||||||
|
public int PageIndex { get; set; }
|
||||||
|
public int PageSize { get; set; }
|
||||||
|
public int Total { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace SonarClient.Models;
|
||||||
|
|
||||||
|
public class PermissionTemplate
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace SonarClient.Models;
|
||||||
|
|
||||||
|
public class PermissionTemplateResponse
|
||||||
|
{
|
||||||
|
public List<PermissionTemplate> PermissionTemplates { get; set; }
|
||||||
|
}
|
||||||
16
ConsoleApps/SonarClient/SonarClient/Models/Project.cs
Normal file
16
ConsoleApps/SonarClient/SonarClient/Models/Project.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
namespace SonarClient.Models;
|
||||||
|
|
||||||
|
public class Project
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string LegacyId { get; set; }
|
||||||
|
public string Key { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Visibility { get; set; }
|
||||||
|
public string OrganizationId { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string[] Tags { get; set; }
|
||||||
|
public Link[] Links { get; set; }
|
||||||
|
public string CreatedAt { get; set; }
|
||||||
|
public string UpdatedAt { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace SonarClient.Models;
|
||||||
|
|
||||||
|
public class ProjectsSearchResponse
|
||||||
|
{
|
||||||
|
public Paging Page { get; set; }
|
||||||
|
public List<Project> Projects { get; set; }
|
||||||
|
}
|
||||||
59
ConsoleApps/SonarClient/SonarClient/Program.cs
Normal file
59
ConsoleApps/SonarClient/SonarClient/Program.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using SonarClient.Actions;
|
||||||
|
using SonarClient.Constants;
|
||||||
|
using SonarClient.Repositories;
|
||||||
|
using SonarClient.Repositories.Interfaces;
|
||||||
|
using Spectre.Console;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
var builder = new ConfigurationBuilder()
|
||||||
|
.AddJsonFile("appsettings.json")
|
||||||
|
.AddUserSecrets<Program>()
|
||||||
|
.AddEnvironmentVariables();
|
||||||
|
var configurationRoot = builder.Build();
|
||||||
|
|
||||||
|
var sonarConfiguration = configurationRoot.GetSection("sonar");
|
||||||
|
|
||||||
|
var token = sonarConfiguration["api_key"];
|
||||||
|
var organizationId = sonarConfiguration["organization_id"];
|
||||||
|
var organizationKey = sonarConfiguration["organization_key"];
|
||||||
|
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
|
||||||
|
services.AddLogging(options =>
|
||||||
|
{
|
||||||
|
options.SetMinimumLevel(LogLevel.Warning);
|
||||||
|
options.AddConsole();
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddHttpClient(SonarConstants.SonarApiV1ClientName, c =>
|
||||||
|
{
|
||||||
|
c.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{token}:")));
|
||||||
|
c.BaseAddress = new Uri(SonarConstants.SonarApiV1BaseAddress);
|
||||||
|
});
|
||||||
|
services.AddHttpClient(SonarConstants.SonarApiV2ClientName, c =>
|
||||||
|
{
|
||||||
|
c.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||||
|
c.BaseAddress = new Uri(SonarConstants.SonarApiV2BaseAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Repositories
|
||||||
|
services.AddTransient<IProjectRepository, ProjectRepository>();
|
||||||
|
services.AddTransient<IPermissionRepository, PermissionRepository>();
|
||||||
|
services.AddTransient<IProjectTagRepository, ProjectTagRepository>();
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
services.AddTransient<UpdatePermissionsAndTags>();
|
||||||
|
|
||||||
|
await using var serviceProvider = services.BuildServiceProvider();
|
||||||
|
|
||||||
|
var updatePermissionsAndTags = serviceProvider.GetRequiredService<UpdatePermissionsAndTags>();
|
||||||
|
|
||||||
|
AnsiConsole.Confirm("Are you sure you want to update permissions?", false);
|
||||||
|
await updatePermissionsAndTags.Execute(organizationId, organizationKey);
|
||||||
|
|
||||||
|
Console.WriteLine("Press any key to exit...");
|
||||||
|
Console.ReadKey();
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using SonarClient.Models;
|
||||||
|
|
||||||
|
namespace SonarClient.Repositories.Interfaces;
|
||||||
|
|
||||||
|
public interface IPermissionRepository
|
||||||
|
{
|
||||||
|
Task<List<PermissionTemplate>> GetPermissionTemplates(string organizationKey);
|
||||||
|
Task ApplyTemplateToProject(string projectKey, string templateId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using SonarClient.Models;
|
||||||
|
|
||||||
|
namespace SonarClient.Repositories.Interfaces;
|
||||||
|
|
||||||
|
public interface IProjectRepository
|
||||||
|
{
|
||||||
|
Task<List<Project>> GetProjects(string organizationId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace SonarClient.Repositories.Interfaces;
|
||||||
|
|
||||||
|
public interface IProjectTagRepository
|
||||||
|
{
|
||||||
|
Task SetTags(string projectKey, string[] tags);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using SonarClient.Constants;
|
||||||
|
using SonarClient.Models;
|
||||||
|
using SonarClient.Repositories.Interfaces;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
|
||||||
|
namespace SonarClient.Repositories;
|
||||||
|
|
||||||
|
public class PermissionRepository(IHttpClientFactory httpClientFactory) : IPermissionRepository
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient = httpClientFactory.CreateClient(SonarConstants.SonarApiV1ClientName);
|
||||||
|
|
||||||
|
public async Task<List<PermissionTemplate>> GetPermissionTemplates(string organizationKey)
|
||||||
|
{
|
||||||
|
var permissionTemplateResponse = await _httpClient.GetFromJsonAsync<PermissionTemplateResponse>($"/api/permissions/search_templates?organization={organizationKey}");
|
||||||
|
return permissionTemplateResponse.PermissionTemplates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ApplyTemplateToProject(string projectKey, string templateId)
|
||||||
|
{
|
||||||
|
var content = new FormUrlEncodedContent([
|
||||||
|
new KeyValuePair<string, string>("projectKey", projectKey),
|
||||||
|
new KeyValuePair<string, string>("templateId", templateId)
|
||||||
|
]);
|
||||||
|
|
||||||
|
var response = await _httpClient.PostAsync("api/permissions/apply_template", content);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using SonarClient.Constants;
|
||||||
|
using SonarClient.Models;
|
||||||
|
using SonarClient.Repositories.Interfaces;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
|
||||||
|
namespace SonarClient.Repositories;
|
||||||
|
|
||||||
|
public class ProjectRepository(IHttpClientFactory httpClientFactory) : IProjectRepository
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient = httpClientFactory.CreateClient(SonarConstants.SonarApiV2ClientName);
|
||||||
|
|
||||||
|
public async Task<List<Project>> GetProjects(string organizationId)
|
||||||
|
{
|
||||||
|
var projects = new List<Project>();
|
||||||
|
var currentPage = 1;
|
||||||
|
ProjectsSearchResponse projectResponse;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
projectResponse = await GetProjects(organizationId, currentPage);
|
||||||
|
projects.AddRange(projectResponse.Projects);
|
||||||
|
currentPage++;
|
||||||
|
} while (projects.Count < projectResponse.Page.Total);
|
||||||
|
|
||||||
|
return projects;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ProjectsSearchResponse> GetProjects(string organizationId, int page)
|
||||||
|
{
|
||||||
|
return await _httpClient.GetFromJsonAsync<ProjectsSearchResponse>(
|
||||||
|
$"/projects/projects?organizationIds={organizationId}&pageIndex={page}"
|
||||||
|
) ?? throw new InvalidOperationException("Failed to retrieve projects from SonarCloud");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using SonarClient.Constants;
|
||||||
|
using SonarClient.Repositories.Interfaces;
|
||||||
|
|
||||||
|
namespace SonarClient.Repositories;
|
||||||
|
|
||||||
|
public class ProjectTagRepository(IHttpClientFactory httpClientFactory) : IProjectTagRepository
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient = httpClientFactory.CreateClient(SonarConstants.SonarApiV1ClientName);
|
||||||
|
|
||||||
|
public async Task SetTags(string projectKey, string[] tags)
|
||||||
|
{
|
||||||
|
var tagsCommaSeperated = string.Join(",", tags);
|
||||||
|
|
||||||
|
var content = new FormUrlEncodedContent([
|
||||||
|
new KeyValuePair<string, string>("project", projectKey),
|
||||||
|
new KeyValuePair<string, string>("tags", tagsCommaSeperated)
|
||||||
|
]);
|
||||||
|
|
||||||
|
var response = await _httpClient.PostAsync("api/project_tags/set", content);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
24
ConsoleApps/SonarClient/SonarClient/SonarClient.csproj
Normal file
24
ConsoleApps/SonarClient/SonarClient/SonarClient.csproj
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<UserSecretsId>e28ed066-de2c-46f8-865f-4b0eda67be3c</UserSecretsId>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
|
||||||
|
<PackageReference Include="Spectre.Console" Version="0.53.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
7
ConsoleApps/SonarClient/SonarClient/appsettings.json
Normal file
7
ConsoleApps/SonarClient/SonarClient/appsettings.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"sonar": {
|
||||||
|
"api_key": "",
|
||||||
|
"organization_id": "aa1160b9-e4ab-4d2a-9ce5-bac8d9b5fb06",
|
||||||
|
"organization_key": "effectory"
|
||||||
|
}
|
||||||
|
}
|
||||||
44
ConsoleApps/SonarClient/SonarClient/sonar-client.md
Normal file
44
ConsoleApps/SonarClient/SonarClient/sonar-client.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# SonarClient Console Application
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
SonarClient is a console application designed to interact with SonarQube/SonarCloud REST APIs. This tool
|
||||||
|
provides functionality to manage, query, and analyze code quality metrics and security findings from SonarQube
|
||||||
|
instances.
|
||||||
|
|
||||||
|
Depending on the operations, the application will reach out to v1 or v2 of the SonarQube API.
|
||||||
|
|
||||||
|
Documentation for the v1 API can be found here: https://sonarcloud.io/web_api
|
||||||
|
Documentation for the v2 API can be found here: https://api-docs.sonarsource.com/
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Update Permissions And Tags**: Update permissions and tags for all projects. Added for migration to new team structure.
|
||||||
|
|
||||||
|
## Operations
|
||||||
|
|
||||||
|
- Retrieve all projects
|
||||||
|
- Set tags for a project
|
||||||
|
- Apply permission template on a project
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Retrieve a personal token from SonarCloud:
|
||||||
|
- Go to: https://sonarcloud.io/account/security
|
||||||
|
- Enter token name
|
||||||
|
- Click `Generate Token`
|
||||||
|
- Copy token
|
||||||
|
2. In Rider
|
||||||
|
- Right-click on the `SonarClient` project
|
||||||
|
- Select `Tools > .NET User Secrets`
|
||||||
|
- This will open `secrets.json`
|
||||||
|
- Add the following:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sonar": {
|
||||||
|
"api_key": "[add token here]"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
3. Run the application
|
||||||
|
4. When done revoke the token
|
||||||
|
|
||||||
47
_azuredevops/cloud-engineering.yml
Normal file
47
_azuredevops/cloud-engineering.yml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
trigger:
|
||||||
|
branches:
|
||||||
|
include:
|
||||||
|
- master
|
||||||
|
pool:
|
||||||
|
vmImage: 'windows-latest'
|
||||||
|
|
||||||
|
variables:
|
||||||
|
repositoryName: $(Build.Repository.Name)
|
||||||
|
buildConfiguration: release
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- stage: build
|
||||||
|
displayName: 'Build for branch policy validation'
|
||||||
|
jobs:
|
||||||
|
- job: build
|
||||||
|
displayName: 'Build solution'
|
||||||
|
steps:
|
||||||
|
- task: SonarCloudPrepare@3
|
||||||
|
inputs:
|
||||||
|
SonarCloud: SonarCloud
|
||||||
|
organization: effectory
|
||||||
|
scannerMode: dotnet
|
||||||
|
projectKey: platform-cloud-engineering
|
||||||
|
projectName: repositoryName
|
||||||
|
extraProperties: |
|
||||||
|
sonar.exclusions=**/obj/**,**/*.dll
|
||||||
|
sonar.cs.vstest.reportsPaths=$(Agent.TempDirectory)/**/*.trx
|
||||||
|
sonar.cs.opencover.reportsPaths=$(Agent.TempDirectory)/**/coverage.opencover.xml
|
||||||
|
sonar.coverage.exclusions=**/*
|
||||||
|
sonar.scm.enabled=true
|
||||||
|
sonar.scm.provider=git
|
||||||
|
sonar.pullrequest.provider=vsts
|
||||||
|
|
||||||
|
- task: DotNetCoreCLI@2
|
||||||
|
displayName: 'dotnet build console apps'
|
||||||
|
inputs:
|
||||||
|
command: build
|
||||||
|
projects: |
|
||||||
|
ConsoleApps/**/*.csproj
|
||||||
|
arguments: '-c $(buildConfiguration)'
|
||||||
|
|
||||||
|
- task: SonarCloudAnalyze@3
|
||||||
|
|
||||||
|
- task: SonarCloudPublish@3
|
||||||
|
inputs:
|
||||||
|
pollingTimeoutSec: '300'
|
||||||
Reference in New Issue
Block a user