jql에 의해 이슈를 불러오는 기능 추가.

This commit is contained in:
2026-01-06 09:36:57 +09:00
committed by seungmuk.oh
parent 3ee4299e24
commit 4cc68b0f20
3 changed files with 102 additions and 2 deletions

View File

@@ -56,6 +56,27 @@ public class VtsService
return issue; return issue;
} }
public async Task<VTSResponse> FetchVtsIssuesByJql(string jql, string targetProject, int startAt, CancellationToken ct)
{
var uri = $"{_appSettings.VTS.ApiBaseUrl}/"
+ $"search?jql={jql} "
+ $"AND project={targetProject} "
+ $"&fields={_appSettings.VTS.SearchFields}"
+ $"&startAt={startAt}&maxResults=50";
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Authorization", $"Bearer {_appSettings.VTS.AccessToken}");
Log.Debug("Fetch VTS issues ready. {fetch_url}", uri);
using var client = new HttpClient();
var response = await client.SendAsync(request, ct).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var jsonResponse = await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
var vtsResponse = JsonSerializer.Deserialize<VTSResponse>(jsonResponse);
return vtsResponse;
}
private List<VtsIssue> JsonDeserializeToVtsIssues(string json) private List<VtsIssue> JsonDeserializeToVtsIssues(string json)
{ {
var result = new List<VtsIssue>(); var result = new List<VtsIssue>();

View File

@@ -1,4 +1,5 @@
using Microsoft.Extensions.Options; using DocumentFormat.OpenXml.Spreadsheet;
using Microsoft.Extensions.Options;
using Serilog; using Serilog;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.ComponentModel; using System.ComponentModel;
@@ -84,6 +85,7 @@ public class MainViewModel : INotifyPropertyChanged
private bool _isWorking = true; private bool _isWorking = true;
public ICommand FetchNowCommand { get; } public ICommand FetchNowCommand { get; }
public ICommand JqlCommand { get; }
public ICommand SaveStatusCommand { get; } public ICommand SaveStatusCommand { get; }
public ICommand ReportCommand { get; } public ICommand ReportCommand { get; }
#endregion Fields #endregion Fields
@@ -126,6 +128,7 @@ public class MainViewModel : INotifyPropertyChanged
_statusTimer.Start(); _statusTimer.Start();
FetchNowCommand = new RelayCommand(_ => FetchNow()); FetchNowCommand = new RelayCommand(_ => FetchNow());
JqlCommand = new RelayCommand(_ => FetchByJqlNow());
SaveStatusCommand = new RelayCommand(_ => { SaveStatusCommand = new RelayCommand(_ => {
UpdateStatusData(); UpdateStatusData();
}); });
@@ -292,6 +295,74 @@ public class MainViewModel : INotifyPropertyChanged
UpdateLabels(); UpdateLabels();
} }
} }
private void FetchByJqlNow()
{
Log.Debug("Start FetchByJqlNow");
// Read jql.txt file
var jqlFilePath = Path.Combine(AppContext.BaseDirectory, "jql.txt");
// If the file does not exist, create an empty file and return.
if (!File.Exists(jqlFilePath))
File.WriteAllText(jqlFilePath, "");
//Read the JQL from the file
var jql = File.ReadAllText(jqlFilePath).Trim();
if (string.IsNullOrEmpty(jql))
{
Log.Debug("End FetchByJqlNow: JQL is empty.");
return;
}
// Start fetching by JQL
Log.Information("Start fetching by JQL: {jql}", jql);
foreach (var project in _appSettings.VTS.TargetProjects)
{
try
{
List<VtsIssue> issues = [];
_sw.Restart();
var vtsResponses = new List<VTSResponse>();
VTSResponse vtsResponse;
vtsResponse = _vts.FetchVtsIssuesByJql(jql, project, 0, _cts.Token).GetAwaiter().GetResult();
vtsResponses.Add(vtsResponse);
var startAt = vtsResponse?.maxResults ?? 0;
var total = vtsResponse?.total ?? 0;
while (startAt < total)
{
vtsResponse = _vts.FetchVtsIssuesByJql(jql, project, startAt, _cts.Token).GetAwaiter().GetResult();
vtsResponses.Add(vtsResponse);
startAt += vtsResponse?.maxResults ?? 50;
}
issues = vtsResponses.SelectMany(r => r.issues.Select(i => new VtsIssue
{
Key = i.key,
Summary = i.fields.summary,
Type = i.fields.issuetype.name,
Status = i.fields.status.name,
Assignee = i.fields.assignee?.displayName ?? "",
Manager = i.fields.reporter?.displayName ?? "",
Due = i.fields.duedate ?? new DateTime(1999, 12, 23, 23, 59, 59),
Updated = i.fields.UpdatedAt,
Parent = i.fields.parent?.key,
Sprint = SprintHelper.ExtractActiveSprintName(i.fields.customfield_10806)
})).ToList();
_sw.Stop();
Log.Information("Fetched {issue_count} issues from VTS in {operation_time}ms"
, issues.Count, _sw.ElapsedMilliseconds);
foreach (var i in issues)
{
i.NeedNotify = (IsNeedNotify(i)) ? 1 : 0;
Log.Debug("{issue_key}: {issue_summary} Updated at {issue_updated}", i.Key, i.Summary, i.Updated);
}
_repo.UpsertVtsIssues(issues, ["Published", "ObjectId"]);
}
catch (Exception ex)
{
Log.Error("Unhandled exception occured during fetching VTS issues: {error_message}", ex.Message);
}
}
Log.Debug("End FetchByJqlNow");
}
#endregion FetchEvents #endregion FetchEvents
#region SaveStatusEvents #region SaveStatusEvents
@@ -411,9 +482,9 @@ public class MainViewModel : INotifyPropertyChanged
var orgIssue = _repo.GetVtsIssueAsync(issue.Key).GetAwaiter().GetResult(); var orgIssue = _repo.GetVtsIssueAsync(issue.Key).GetAwaiter().GetResult();
if (orgIssue == null) // A new issue assigned to me. if (orgIssue == null) // A new issue assigned to me.
{ {
Log.Debug("{method_name} - A new issue({target_keys}) assigned to me.", nameof(IsNeedNotify), issue.Key, issue.Assignee);
if (issue.Assignee == MyName) if (issue.Assignee == MyName)
{ {
Log.Debug("{method_name} - A new issue({target_keys}) assigned to me.", nameof(IsNeedNotify), issue.Key, issue.Assignee);
_publishedIssuesQueue.Enqueue(issue); _publishedIssuesQueue.Enqueue(issue);
return true; return true;
} }

View File

@@ -26,6 +26,7 @@ public partial class MainWindow : Window
var contextMenu = new ContextMenuStrip(); var contextMenu = new ContextMenuStrip();
contextMenu.Items.Add("Open", null, (s, e) => ShowWindow()); contextMenu.Items.Add("Open", null, (s, e) => ShowWindow());
contextMenu.Items.Add("Fetch", null, (s, e) => FetchNow()); contextMenu.Items.Add("Fetch", null, (s, e) => FetchNow());
contextMenu.Items.Add("JQL", null, (s, e) => FetchByJqlNow());
contextMenu.Items.Add("Save status", null, (s, e) => SaveStatusNow()); contextMenu.Items.Add("Save status", null, (s, e) => SaveStatusNow());
contextMenu.Items.Add("Report", null, (s, e) => ReportNow()); contextMenu.Items.Add("Report", null, (s, e) => ReportNow());
contextMenu.Items.Add("Exit", null, (s, e) => ExitApp()); contextMenu.Items.Add("Exit", null, (s, e) => ExitApp());
@@ -49,6 +50,13 @@ public partial class MainWindow : Window
vm.FetchNowCommand.Execute(null); vm.FetchNowCommand.Execute(null);
} }
} }
private void FetchByJqlNow()
{
if (DataContext is MainViewModel vm)
{
vm.JqlCommand.Execute(null);
}
}
private void SaveStatusNow() private void SaveStatusNow()
{ {
if (DataContext is MainViewModel vm) if (DataContext is MainViewModel vm)