JiraIssue에 대해 Trilium 노트 발급 기능 추가.

This commit is contained in:
2025-12-12 17:21:26 +09:00
parent 2e2edc8dac
commit f6bccc5434
7 changed files with 189 additions and 10 deletions

View File

@@ -17,4 +17,19 @@ public class JiraIssue
public DateTimeOffset Published { get; set; }
public string? ObjectId { get; set; }
public int NeedNotify { get; set; }
public JiraIssue() { }
protected JiraIssue(JiraIssue issue)
{
this.Key = issue.Key;
this.Summary = issue.Summary;
this.Parent = issue.Parent;
this.Type = issue.Type;
this.Status = issue.Status;
this.Assignee = issue.Assignee;
this.Manager = issue.Manager;
this.Published = issue.Published;
this.ObjectId = issue.ObjectId;
this.NeedNotify = issue.NeedNotify;
}
}

View File

@@ -15,9 +15,9 @@ public class Trilium
{
public string EtapiToken { get; set; } = string.Empty;
public string EtapiBaseUrl { get; set; } = "https://localhost/etapi";
public string JiraOpenedIssuePageId = string.Empty;
public string JiraWorkingIssuePageId = string.Empty;
public string JiraResolvedIssuePageId = string.Empty;
public string JiraOpenedIssuePageId { get; set; } = string.Empty;
public string JiraWorkingIssuePageId { get; set; } = string.Empty;
public string JiraResolvedIssuePageId { get; set; } = string.Empty;
public string DividendPageId { get; set; } = String.Empty;
}

29
Models/TriliumNote.cs Normal file
View File

@@ -0,0 +1,29 @@
using System.Text.Json;
using TriliumMind.Data.Entities;
namespace TriliumMind.Models;
public class TriliumNote : JiraIssue
{
public string ParentNoteId { get; set; } = string.Empty;
public TriliumResponse? TriliumNoteData { get; set; } = null;
public TriliumNote(JiraIssue issue, string parentNoteId) : base(issue)
{
this.ParentNoteId = parentNoteId;
}
public StringContent ToNoteContent()
{
var noteData = new
{
parentNoteId = this.ParentNoteId,
title = $"{this.Key} {this.Summary}",
type = "text",
content = ""
};
var jsonContent = JsonSerializer.Serialize(noteData);
return new StringContent(jsonContent, null, "application/json");
}
}

48
Models/TriliumResponse.cs Normal file
View File

@@ -0,0 +1,48 @@

public class TriliumResponse
{
public Note note { get; set; }
public Branch branch { get; set; }
}
public class Note
{
public string noteId { get; set; }
public bool isProtected { get; set; }
public string title { get; set; }
public string type { get; set; }
public string mime { get; set; }
public string blobId { get; set; }
public string dateCreated { get; set; }
public string dateModified { get; set; }
public string utcDateCreated { get; set; }
public string utcDateModified { get; set; }
public string[] parentNoteIds { get; set; }
public object[] childNoteIds { get; set; }
public string[] parentBranchIds { get; set; }
public object[] childBranchIds { get; set; }
public Attribute[] attributes { get; set; }
}
public class Attribute
{
public string attributeId { get; set; }
public string noteId { get; set; }
public string type { get; set; }
public string name { get; set; }
public string value { get; set; }
public int position { get; set; }
public bool isInheritable { get; set; }
public string utcDateModified { get; set; }
}
public class Branch
{
public string branchId { get; set; }
public string noteId { get; set; }
public string parentNoteId { get; set; }
public object prefix { get; set; }
public int notePosition { get; set; }
public bool isExpanded { get; set; }
public string utcDateModified { get; set; }
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Reflection.Metadata;
using TriliumMind.Data.Entities;
using TriliumMind.Models;
@@ -26,7 +27,6 @@ public static class JiraIssueMapper
NeedNotify = 0
};
}
private static string CleanDisplayName(string? displayName)
{
if (string.IsNullOrWhiteSpace(displayName))

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text;
using TriliumMind.Data.Entities;
using TriliumMind.Models;
namespace TriliumMind.Services;
@@ -18,7 +19,73 @@ public class TriliumService
_log = Log.ForContext<TriliumService>();
_configs = configs;
}
public async Task<string> FettchPageContentsAsync(Issue issue)
public async Task<IEnumerable<JiraIssue>> PublishNotesAsync(IEnumerable<JiraIssue> jiraIssues)
{
var issuesToPatch = new List<JiraIssue>();
var issuesToPost = new List<JiraIssue>();
foreach (var jiraIssue in jiraIssues)
{
if(jiraIssue.Published > DateTimeOffset.MinValue)
issuesToPatch.Add(jiraIssue);
else
issuesToPost.Add(jiraIssue);
break;
}
// Post note does not support attribute, so additional work is required.
var postedNotes = await PostNotesAsync(issuesToPost);
//issuesToPatch.AddRange(postedIssues);
//var patchedIssues = await PatchNotesAsync(issuesToPatch);
return issuesToPatch;
}
public async Task<IEnumerable<TriliumNote>> PostNotesAsync(IEnumerable<JiraIssue> jiraIssues)
{
var baseUrl = _configs.AppSettings.Trilium.EtapiBaseUrl;
var token = _configs.AppSettings.Trilium.EtapiToken;
var apiUrl = $"{baseUrl}/create-note";
var triliumNotes = new List<TriliumNote>();
var lastRequestTime = DateTimeOffset.MinValue;
foreach (var jiraIssue in jiraIssues)
{
var timeSinceLastRequest = DateTimeOffset.UtcNow - lastRequestTime;
if (timeSinceLastRequest < TimeSpan.FromSeconds(1))
{
var delayTime = TimeSpan.FromSeconds(1) - timeSinceLastRequest;
await Task.Delay(delayTime);
}
var note = new TriliumNote(jiraIssue, GetParentPageId(jiraIssue.Status));
var request = new HttpRequestMessage(HttpMethod.Post, apiUrl);
request.Headers.TryAddWithoutValidation("Authorization", token);
request.Content = note.ToNoteContent();
using var client = new HttpClient();
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
// Deserialize to TriliumResponse
var triliumResponse = await response.Content.ReadFromJsonAsync<TriliumResponse>();
lastRequestTime = DateTimeOffset.UtcNow;
note.ObjectId = triliumResponse?.note.noteId;
note.Published = lastRequestTime;
note.TriliumNoteData = triliumResponse;
triliumNotes.Add(note);
}
return triliumNotes;
}
public Task<IEnumerable<JiraIssue>> PatchNotesAsync(IEnumerable<JiraIssue> jiraIssues)
{
throw new NotImplementedException();
// Update attributes
}
public async Task<string> FetchPageContentsAsync(Issue issue)
{
var baseUrl = _configs.AppSettings.Trilium.EtapiBaseUrl;
var pageId = "QxxFqCNAtIOy";
@@ -34,4 +101,26 @@ public class TriliumService
// Return new page ID after creating a new page
return "";
}
private string GetParentPageId(string issueStatus)
{
var readyPageId = _configs.AppSettings.Trilium.JiraOpenedIssuePageId;
var workingPageId = _configs.AppSettings.Trilium.JiraWorkingIssuePageId;
var resolvedPageId = _configs.AppSettings.Trilium.JiraResolvedIssuePageId;
string pageId = string.Empty;
if (issueStatus.Equals("Open", StringComparison.OrdinalIgnoreCase)
|| issueStatus.Equals("Reopend", StringComparison.OrdinalIgnoreCase))
{
pageId = readyPageId;
}
else if (issueStatus.Equals("In progress", StringComparison.OrdinalIgnoreCase))
{
pageId = workingPageId;
}
else
{
pageId = resolvedPageId;
}
return pageId;
}
}

View File

@@ -12,7 +12,7 @@ public class TriliumWorker : BackgroundService
private readonly Serilog.ILogger _log;
private readonly AppConfigs _config;
private readonly IServiceScopeFactory _scopeFactory; // Singleton services cannot directly inject Scoped services.
private readonly TriliumService _triliumService;
private readonly TriliumService _trilium;
private readonly Channel<Issue> _issueChannel;
public TriliumWorker(AppConfigs configs, IServiceScopeFactory serviceScopeFactory,
@@ -21,7 +21,7 @@ public class TriliumWorker : BackgroundService
_log = Log.ForContext<TriliumWorker>();
_config = configs;
_scopeFactory = serviceScopeFactory;
_triliumService = triliumService;
_trilium = triliumService;
_issueChannel = issueChannel;
}
@@ -35,9 +35,7 @@ public class TriliumWorker : BackgroundService
var db = scope.ServiceProvider.GetRequiredService<AppDbService>();
var unpublishedIssues = await db.GetUnpublishedJiraIssuesAsync(stoppingToken);
if (unpublishedIssues != null)
{
// Publish or update Trilium notes
}
await _trilium.PublishNotesAsync(unpublishedIssues);
await Task.Delay(10 *1000, stoppingToken);
}