feat: добавить поддержку GitLab (api, clients, tools) и обновить документацию
This commit is contained in:
169
LazyBear.MCP/Services/GitLab/GitLabToolsBase.cs
Normal file
169
LazyBear.MCP/Services/GitLab/GitLabToolsBase.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user