169 lines
5.5 KiB
C#
169 lines
5.5 KiB
C#
using System.Collections.Generic;
|
||
using System.Text.Json;
|
||
using LazyBear.MCP.Services.ToolRegistry;
|
||
using RestSharp;
|
||
|
||
namespace LazyBear.MCP.Services.GitLab;
|
||
|
||
/// <summary>
|
||
/// Базовый класс для всех инструментов GitLab
|
||
/// </summary>
|
||
public sealed class GitLabToolsBase
|
||
{
|
||
protected readonly GitLabApiClient _client;
|
||
protected readonly string _baseUrl;
|
||
protected readonly int _perPageDefault;
|
||
|
||
private readonly string _token;
|
||
private readonly string _baseUrlConfig;
|
||
private readonly ToolRegistryService _registry;
|
||
|
||
/// <summary>
|
||
/// Ошибка инициализации клиента (если возникла)
|
||
/// </summary>
|
||
protected string? ClientInitializationError { get; }
|
||
|
||
/// <summary>
|
||
/// Конструктор
|
||
/// </summary>
|
||
/// <param name="baseUrlConfig">Конфигурация URL</param>
|
||
/// <param name="token">API токен</param>
|
||
/// <param name="registry">Регистратор инструментов</param>
|
||
public GitLabToolsBase(
|
||
string baseUrlConfig,
|
||
string token,
|
||
ToolRegistryService registry)
|
||
{
|
||
_token = token;
|
||
_baseUrlConfig = baseUrlConfig;
|
||
_registry = registry;
|
||
|
||
// Инициализация клиента
|
||
_baseUrl = baseUrlConfig;
|
||
_client = new GitLabApiClient(baseUrlConfig);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Проверка, активирован ли инструмент в TUI
|
||
/// </summary>
|
||
protected bool TryCheckEnabled(string toolName, out string error)
|
||
{
|
||
if (!_registry.IsToolEnabled("GitLab", toolName))
|
||
{
|
||
error = $"Инструмент '{toolName}' модуля GitLab отключён в TUI.";
|
||
return false;
|
||
}
|
||
error = string.Empty;
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получение клиента RestSharp
|
||
/// </summary>
|
||
protected bool TryGetClient(out GitLabApiClient client, out string error)
|
||
{
|
||
client = _client;
|
||
error = ClientInitializationError is null
|
||
? string.Empty
|
||
: $"GitLab клиент не инициализирован. Проверьте GitLab:Url. Детали: {ClientInitializationError}";
|
||
return ClientInitializationError is null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Создание запроса к GitLab API
|
||
/// </summary>
|
||
protected RestRequest CreateRequest(string resource, RestSharp.Method method = RestSharp.Method.Get)
|
||
{
|
||
var request = _client.GetRequest(resource);
|
||
return request;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Форматирование ошибки ответа от GitLab API
|
||
/// </summary>
|
||
protected string FormatResponseError(string toolName, RestResponse response, string? resource = null)
|
||
{
|
||
var resourcePart = string.IsNullOrWhiteSpace(resource)
|
||
? string.Empty
|
||
: $", resource='{resource}'";
|
||
var body = string.IsNullOrWhiteSpace(response.Content)
|
||
? "-"
|
||
: response.Content;
|
||
return $"Ошибка GitLab в tool '{toolName}'{resourcePart}: status={(int)response.StatusCode}, details={body}";
|
||
}
|
||
|
||
/// <summary>
|
||
/// Форматирование исключения
|
||
/// </summary>
|
||
protected string FormatException(string toolName, Exception exception, string? resource = null)
|
||
{
|
||
var resourcePart = string.IsNullOrWhiteSpace(resource)
|
||
? string.Empty
|
||
: $", resource='{resource}'";
|
||
return $"Ошибка GitLab в tool '{toolName}'{resourcePart}: {exception.GetType().Name}: {exception.Message}";
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получение вложенного строки из Json
|
||
/// </summary>
|
||
protected static string? GetNestedString(JsonElement element, params string[] path)
|
||
{
|
||
var current = element;
|
||
|
||
foreach (var segment in path)
|
||
{
|
||
if (!current.TryGetProperty(segment, out current))
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
return current.ValueKind == JsonValueKind.String
|
||
? current.GetString()
|
||
: current.ToString();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Экстракция текста из комментариев GitLab
|
||
/// </summary>
|
||
protected static string ExtractCommentText(JsonElement body)
|
||
{
|
||
var chunks = new List<string>();
|
||
CollectText(body, chunks);
|
||
return chunks.Count == 0 ? "-" : string.Join(" ", chunks);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Рекурсивный сбор текста из JSON
|
||
/// </summary>
|
||
protected static void CollectText(JsonElement element, List<string> chunks)
|
||
{
|
||
if (element.ValueKind == JsonValueKind.Object)
|
||
{
|
||
// Ищем текстовый узел в структуре комментария GitLab
|
||
if (element.TryGetProperty("body", out var bodyElement) &&
|
||
bodyElement.TryGetProperty("text", out var textElement))
|
||
{
|
||
if (textElement.ValueKind == JsonValueKind.String)
|
||
{
|
||
chunks.Add(textElement.GetString() ?? string.Empty);
|
||
}
|
||
}
|
||
|
||
foreach (var property in element.EnumerateObject())
|
||
{
|
||
CollectText(property.Value, chunks);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
if (element.ValueKind == JsonValueKind.Array)
|
||
{
|
||
foreach (var item in element.EnumerateArray())
|
||
{
|
||
CollectText(item, chunks);
|
||
}
|
||
}
|
||
}
|
||
} |