Newtonsoft.Json
RestSharp
RestSharp
Newtonsoft.Json
Newtonsoft.Json.Linq
RestSharp.Authenticators
Newtonsoft.Json.Converters
System.Dynamic
System
System.Linq.Dynamic
System.Web
private string BaseUrl = "https://sonarqube.sdt.local";
private Dictionary ruleDictionary;
//private DateTime BeginningOfTheYear = new DateTime(DateTime.Now.Year, 1, 1);
private static DateTime CreatedBefore = new DateTime(2020, 8, 13);
private static DateTime StartOfSprint = CreatedBefore.AddDays(1);
private static DateTime EndOfSprint = CreatedBefore.AddDays(15);
private Dictionary Parameters = new Dictionary
{
{ "projects", ""},
// {"types","BUG,VULNERABILITY,SECURITY_HOTSPOT"},
// {"severities","BLOCKER,CRITICAL,MAJOR"},
// {"statuses", "OPEN,CONFIRMED,REOPENED,TO_REVIEW,IN_REVIEW"},
{"resolutions", "FIXED"},
{"s", "CLOSE_DATE"},
{"languages", null},
{"createdBefore", $"{CreatedBefore:yyyy-MM-dd}"},
//{"createdAfter", null}, // $"{BeginningOfTheYear:yyyy-MM-dd}"
{"assignees", null}
};
#region Enumerations
public string GetName(Type enumType, int value)
{
return Enum.GetName(enumType, value);
}
public enum SonarType
{
BUG,
VULNERABILITY,
CODE_SMELL,
SECURITY_HOTSPOT
}
public enum SonarSeverity
{
BLOCKER,
CRITICAL,
MAJOR,
MINOR,
INFO
}
public enum QubooSeverity
{
BLOCKER = 10,
CRITICAL = 7,
MAJOR = 5,
MINOR = 2,
INFO = 1
}
public enum SonarResolution
{
UNRESOLVED,
FIXED,
WONTFIX,
FALSE_POSITIVE,
REMOVED
}
public enum SonarStatus
{
OPEN,
TO_REVIEW,
IN_REVIEW,
REVIEWED,
REOPENED,
CONFIRMED,
RESOLVED,
CLOSED
}
#endregion
public Dictionary> GroupUsers = new Dictionary>();
void Main()
{
RestClient client = new RestClient();
// var projects = ListProjects(client);
// projects.Dump();
// return;
new { CreatedBefore, StartOfSprint, EndOfSprint}.Dump("Campaign");
PopulateUserGroups();
var value = GetName(typeof(SonarType), (int)SonarType.BUG);
//GetJson(client, "/api/languages/list").Dump();
//ListWebServices(client).Dump("List WebServices");
var rules = GetRules(client)
.OrderBy(l => l.repo).ThenBy(l => l.key);
ruleDictionary = rules
.ToDictionary(r => r.key, r => $"{r.name} ({r.severity})");
var parameters = new List {
new RestSharp.Parameter("additionalFields","_all", ParameterType.QueryString)
};
parameters.AddRange(Parameters.Where(p => !string.IsNullOrEmpty(p.Value))
.Select(p => new RestSharp.Parameter(p.Key, p.Value, ParameterType.QueryString)));
var issues = GetIssues(client, parameters)
.Where(i => i.closeDate >= StartOfSprint && i.closeDate <= EndOfSprint)
// .Where(i => i.closeDate >= CreatedBefore.AddDays(-14) && i.closeDate <= CreatedBefore)
;
if (1 == 0)
{
if (parameters.Select(p => p.Name).Contains("projects"))
{
$"{parameters.First(p => p.Name == "projects").Value} ({issues.Count()} Issues)".Dump();
}
else
{
$"{issues.Count()} Issues".Dump();
}
}
issues
.Where(i => i.assignee != null)
.Select(i => new
{
i.severity,
i.project,
i.type,
i.status,
i.resolution,
i.assignee,
team = GetUserGroup(i.assignee),
i.debt,
i.effort,
i.closeDate,
score = Score(i.severity, i.debt)
})
//.GroupBy(i => new {i.assignee, i.severity, i.type}, (g,d) => d)
.OrderBy(i => i.team)
.ThenBy(i => i.project)
.ThenBy(i => i.assignee)
.ThenBy(i => i.severity)
.ThenBy(i => i.type)
.Dump();
//issues.Take(10).Dump("Dump 10 issues");
//ReportIssues(issues);
}
public int Score(string severity, string debt, int items = 1)
{
if (debt == null || severity == null) return 0;
var qSeverity = (QubooSeverity)Enum.Parse(typeof(QubooSeverity), severity);
var iDebt = int.Parse(Regex.Replace(debt, @"[^\d]", ""));
return Score(qSeverity, iDebt, items);
}
public int Score(QubooSeverity severity, int debt, int items = 1)
{
//$"Math.Max(1, {debt} / 10 * {(int)severity}) * {items} items".Dump();
return Math.Max(1, debt / 10 * (int)severity) * items;
}
void ReportIssues(IEnumerable issues)
{
var byTeam = issues
.OrderBy(i => GetUserGroup(i.assignee ?? i.author ?? ""))
.ThenBy(i => i.component)
.ThenBy(i => i.type)
.ThenBy(i => i.severity)
.Select(i => new
{
assignee = GetUserGroup(i.assignee ?? i.author ?? "Not assigned"),
i.component,
i.message,
i.type,
i.status,
i.severity,
i.effort,
i.creationDate,
i.closeDate,
lineNumber = i.line,
i.taglist
})
.GroupBy(i => i.assignee, (g, d) => new TeamGrouper(
g,
d.GroupBy(dd => new { type = Path.GetExtension(dd.component), dd.component }, (gg, gd) =>
new ComponentGrouper(
gg.type,
gg.component,
gd.GroupBy(yy => new
{
yy.creationDate.Year,
yy.creationDate.Month,
yy.type,
yy.status,
yy.severity
}, (gdk, gdd) =>
new SummaryGrouper(
gdk.Year,
gdk.Month,
gdk.status,
gdk.type,
gdk.severity,
gdd.Count()
)).OrderByDescending(yy => yy.Year)
.ThenByDescending(yy => yy.Month)
))
.GroupBy(dd => dd.Solution, (xg, xd) => new SolutionGrouper
(
xg,
xd.GroupBy(y => y.Project, (yg, yd) => new ProjectGrouper
(
yg,
yd.OrderBy(x => x.Path).ThenBy(x => x.FileName)
)).OrderBy(x => x.Project)
))
.OrderBy(dd => dd.Solution)
))
.OrderBy(i => i.Team).ToList();
byTeam.ForEach(team =>
{
team.Solutions.ForEach(solution =>
{
solution.Solution.Dump().DumpX(team.Team); ;
solution.Projects.Dump(10).DumpX(team.Team);
});
});
}
#region User Group Definition
public void PopulateUserGroups()
{
GroupUsers.Add("Architecture", new List { });
GroupUsers.Add("The A-Team", new List { "tlamb", "tdebolt" });
GroupUsers.Add("Ryan King", new List { "rreimer", "jpollard", "chelm", "aschey" });
GroupUsers.Add("Cube Killers", new List { "yrhee", "dforero", "lbehmer" });
GroupUsers.Add("Kobra Kaizer", new List { "mschmidt", "nbuckle", "fsweis", "jmorales" });
GroupUsers.Add("Team Sloth", new List { "myoung", "bkrehl", "nnawale", "lrenadesouza", "nleroy" });
GroupUsers.Add("Team Thanos", new List { "bwells", "zbruin", "zhare", "mwynne", "mdeboer", "jnarofsky" });
GroupUsers.Add("Platform", new List { "mleitch", "awolek", "jrapp", "cbello", "jchau", "hgutta", "ljian" });
GroupUsers.Add("Boo", new List { "kferchuk", "akuten", "hvladyka", "vfay" });
GroupUsers.Add("Jedi", new List { "akost", "sdominska", "tparashchak", "opalamar" });
GroupUsers.Add("Pokemon", new List { "okrainyk", "mstepanov", "yantoniuk" });
GroupUsers.Add("Tropic Thunder", new List { "ainzhyievskyi", "ihalenok", "dpanteliuk", "akorchynskyi" });
GroupUsers.Add("Bee Swarmers", new List { "tscanlan", "cburke", "sxie" });
GroupUsers.Add("The Moustache", new List { "wrusinko" });
}
public string GetUserGroup(string user)
{
var group = GroupUsers.Where(gu => gu.Value.Contains(user)).FirstOrDefault();
return group.Key ?? $"unknown {user}";
}
#endregion
#region Issues Report
public class SolutionGrouper
{
public string Solution { get; set; }
public List Projects { get; set; }
public SolutionGrouper(string solution, IOrderedEnumerable projects)
{
Solution = solution;
Projects = projects.ToList();
}
}
public class ProjectGrouper
{
public string Project { get; set; }
public IOrderedEnumerable Issues { get; set; }
public ProjectGrouper(string project, IOrderedEnumerable issues)
{
Project = project;
Issues = issues;
}
}
public class TeamGrouper
{
public string Team { get; set; }
public List Solutions { get; set; }
public TeamGrouper(string team, IOrderedEnumerable solutions)
{
Team = team;
Solutions = solutions.ToList();
}
}
public class ComponentGrouper
{
public string Solution { get; set; }
public List Issues { get; set; }
public string Component { get; set; }
public string Path { get; set; }
public string Project { get; set; }
public string FileName { get; set; }
public string Extension { get; set; }
public string Url { get; set; }
public ComponentGrouper()
{
}
public ComponentGrouper(string type, string component, IOrderedEnumerable issues)
{
Component = component.Split(new[] { ':' }).Last().Replace('/', '\\');
Solution = component.Split(new[] { ':' }).First();
Extension = type.TrimStart(new[] { '.' });
var filePath = $@"D:\{Component}";
Path = System.IO.Path.GetDirectoryName(filePath);
var pathParts = Path.Split(System.IO.Path.DirectorySeparatorChar);
//pathParts.Dump();
if (new[] { "Code", "Internal Tools", "src" }.Contains(pathParts[1]))
{
Project = pathParts.Skip(2).First();
Path = string.Join($"{System.IO.Path.DirectorySeparatorChar}", pathParts.Skip(2));
}
else
{
Project = pathParts.Skip(1).First();
Path = string.Join($"{System.IO.Path.DirectorySeparatorChar}", pathParts.Skip(1));
}
FileName = System.IO.Path.GetFileName(filePath);
Issues = issues.ToList();
Url = HttpUtility.HtmlEncode($@"{component}");
}
object ToDump() => new
{
Issues,
Path,
FileName = new Hyperlinq($@"https://sonarqube.sdt.local/code?id={Url}", FileName)
};
}
public class SummaryGrouper
{
public int Year { get; set; }
public int Month { get; set; }
public string Status { get; set; }
public string Type { get; set; }
public string Severity { get; set; }
public int Items { get; set; }
public SummaryGrouper()
{
}
public SummaryGrouper(int year, int month, string status, string type, string severity, int count)
{
Year = year;
Month = month;
Status = status;
Type = type;
Severity = severity;
Items = count;
}
object ToDump() => new
{
Date = $"{(new DateTime(Year, Month, 1)):yyyy/MM}",
Status,
Type,
Severity,
Count = $"{Items}"
};
}
#endregion
public void WriteReport(List issues)
{
var reports = issues
.GroupBy(sdm => new
{
author = sdm.author ?? sdm.assignee ?? "",
project = sdm.project,
componentKey = sdm.componentKey,
status = sdm.status ?? "",
severity = sdm.severity ?? "",
resolution = sdm.resolution ?? "",
type = sdm.type,
rule = sdm.rule
}, (g, d) => new IssueReport
{
group = new IssueGroup
{
author = g.author,
project = g.project,
componentKey = g.componentKey,
status = g.status,
severity = g.severity,
resolution = g.resolution,
type = g.type,
rule = g.rule,
issues = d.Count()
},
issues = d.Select(issue => (IssueReportIssue)issue).ToList()
})
.OrderBy(r => r.group.author)
.ThenBy(r => r.group.project)
.ToList();
reports.ForEach(report =>
{
//"".PadLeft(40, '―').Dump();
// report.group.Dump();
var issueList = report.issues
.GroupBy(i => new { i.message, i.debt, i.effort, i.fileType, i.assignee, i.comment }, (g, d) => new
{
g.message,
debt = g.debt * d.Count(),
effort = g.effort * d.Count(),
g.fileType,
g.assignee,
g.comment,
createdAt = $"{d.Min(i => i.createdAt):d}",
closedAt = $"{d.Max(i => i.closedAt):d}",
daysToClose = (int)d.Max(i => i.closedAt).Subtract(d.Min(i => i.createdAt)).TotalDays,
occurred = d.Count()
});
new
{
report.group,
issueList
}.Dump();
// if (issueList.Count() > 1000)
// {
// issueList
// .Select((issue, index) => new { issue, index })
// .GroupBy(x => x.index / 1000, (key, y) => y.Select(z => z.issue))
// .Dump();
// }
// else
// {
// issueList.Dump();
// }
});
}
public void SummarizeIssues(RestClient client)
{
var types = new[] { "VULNERABILITY", "BUG", "CODE_SMELL" };
var severities = new[] { "BLOCKER", "CRITICAL", "MAJOR" };
var issues = GetIssues(client);
var CiStarterSet = issues
.Where(i => i.status != "CLOSED"
&& new[] { "BLOCKER", "CRITICAL", "MAJOR" }.Contains(i.severity)
);
CiStarterSet
.OrderBy(i => i.rule == "csharpsquid:S3776" ? 99
: i.severity == "MAJOR" ? 2
: i.severity == "BLOCKER" ? 0
: i.severity == "CRITICAL" ? 1 : 99)
.GroupBy(i => i.component
, (g, d) => new
{
key = g,
critical = d.Count(x => x.severity == "CRITICAL"),
ageInYears = (int)(d.Min(x => DateTime.Now.Subtract(x.creationDate).TotalDays) / 365),
team = ""
})
.OrderByDescending(i => i.ageInYears)
.ThenBy(i => i.critical)
.ThenBy(i => i.key)
//.ThenBy(i => i.key)
.Dump();
}
JObject GetVersion(RestClient client)
{
return GetJson(client, "/api/server/system");
}
#region Rule Management
List GetRules(RestClient client)
{
List rules = new List();
var pageNo = 1;
while (true)
{
var info = ListRules(client, pageNo);
if (info == null) break;
var deserialized = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(info)).rules;
if (deserialized == null || !deserialized.Any()) break;
pageNo += 1;
var page = deserialized; //.Where(i => i.rule.StartsWith("csharpsquid:"));
if (!page.Any()) continue;
rules.AddRange(page);
}
return rules;
}
JObject ListRules(RestClient client, int page = 1, int pageSize = 500)
{
return GetJson(client, "/api/rules/search", new List {
new Parameter("ps", pageSize, ParameterType.QueryString),
new Parameter("p", page, ParameterType.QueryString)
});
}
public class Rule
{
public string key { get; set; }
public string repo { get; set; }
public string name { get; set; }
public DateTime createdAt { get; set; }
public string htmlDesc { get; set; }
public string mdDesc { get; set; }
public string severity { get; set; }
public bool isBlockerIssue => severity == "BLOCKER";
public bool isCriticalIssue => severity == "CRITICAL";
public bool isMajorIssue => severity == "MAJOR";
public string status { get; set; }
public string internalKey { get; set; }
public bool isTemplate { get; set; }
public List