feat: добавить поддержку GitLab (api, clients, tools) и обновить документацию
This commit is contained in:
607
LazyBear.MCP/Services/GitLab/GitLabMergeRequestTools.cs
Normal file
607
LazyBear.MCP/Services/GitLab/GitLabMergeRequestTools.cs
Normal file
@@ -0,0 +1,607 @@
|
||||
using System.ComponentModel;
|
||||
using System.Text.Json;
|
||||
using LazyBear.MCP.Services.ToolRegistry;
|
||||
using ModelContextProtocol.Server;
|
||||
using RestSharp;
|
||||
|
||||
namespace LazyBear.MCP.Services.GitLab;
|
||||
|
||||
public sealed class GitLabMergeRequestTools(
|
||||
GitLabClientProvider provider,
|
||||
IConfiguration configuration,
|
||||
ToolRegistryService registry)
|
||||
{
|
||||
private readonly string _token = configuration["GitLab:Token"] ?? string.Empty;
|
||||
private readonly string _baseUrl = configuration["GitLab:Url"] ?? string.Empty;
|
||||
private const string ModuleName = "GitLab";
|
||||
|
||||
private bool TryCheckEnabled(string toolName, out string error)
|
||||
{
|
||||
if (!registry.IsToolEnabled(ModuleName, toolName))
|
||||
{
|
||||
error = $"Инструмент '{toolName}' модуля GitLab отключён в TUI.";
|
||||
return false;
|
||||
}
|
||||
error = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryGetClient(out RestSharp.IRestClient client, out string error)
|
||||
{
|
||||
if (provider.InitializationError is not null)
|
||||
{
|
||||
client = null!;
|
||||
error = $"GitLab клиент не инициализирован. Детали: {provider.InitializationError}";
|
||||
return false;
|
||||
}
|
||||
|
||||
var clientInstance = provider.GetClient();
|
||||
if (clientInstance is null)
|
||||
{
|
||||
client = null!;
|
||||
error = "GitLab клиент не создан.";
|
||||
return false;
|
||||
}
|
||||
|
||||
client = clientInstance.RestClient;
|
||||
error = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
private 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}";
|
||||
}
|
||||
|
||||
private 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}";
|
||||
}
|
||||
|
||||
private static string GetVisibility(string visibility) => visibility switch
|
||||
{
|
||||
"public" => "Public",
|
||||
"internal" => "Internal",
|
||||
"private" => "Private",
|
||||
_ => visibility ?? "unknown"
|
||||
};
|
||||
|
||||
private 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();
|
||||
}
|
||||
|
||||
private static string GetState(string state) => state switch
|
||||
{
|
||||
"opened" => "Opened",
|
||||
"merged" => "Merged",
|
||||
"closed" => "Closed",
|
||||
"declined" => "Declined",
|
||||
_ => state ?? "unknown"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Получить список MR
|
||||
/// </summary>
|
||||
/// <param name="projectId">ID проекта</param>
|
||||
/// <param name="cancellationToken">Token отмены</param>
|
||||
[McpServerTool, Description("Получить список Merge Requests")]
|
||||
public async Task<string> ListMergeRequests(
|
||||
[Description("ID проекта")] int projectId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!TryCheckEnabled("ListMergeRequests", out var enabledError)) return enabledError;
|
||||
|
||||
if (projectId <= 0)
|
||||
{
|
||||
return "ID проекта GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (!TryGetClient(out var client, out var error)) return error;
|
||||
|
||||
try
|
||||
{
|
||||
var request = new RestRequest($"/projects/{projectId}/merge_requests", RestSharp.Method.Get);
|
||||
request.AddHeader("Accept", "application/json");
|
||||
request.AddHeader("PRIVATE-TOKEN", _token);
|
||||
request.AddQueryParameter("per_page", "30");
|
||||
|
||||
var response = await client.ExecuteAsync(request, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
|
||||
{
|
||||
return FormatResponseError("list_merge_requests", response, $"/projects/{projectId}/merge_requests");
|
||||
}
|
||||
|
||||
using var document = JsonDocument.Parse(response.Content);
|
||||
var root = document.RootElement;
|
||||
|
||||
if (!root.TryGetProperty("merge_requests", out var mrElement) || mrElement.GetArrayLength() == 0)
|
||||
{
|
||||
return $"Merge Request в проекте #{projectId} не найдены.";
|
||||
}
|
||||
|
||||
var lines = new List<string>();
|
||||
foreach (var mr in mrElement.EnumerateArray())
|
||||
{
|
||||
var iid = GetNestedString(mr, "iid") ?? "-";
|
||||
var title = GetNestedString(mr, "title") ?? "-";
|
||||
var state = GetState(GetNestedString(mr, "state") ?? "");
|
||||
var sourceBranch = GetNestedString(mr, "source", "branch") ?? "-";
|
||||
var targetBranch = GetNestedString(mr, "target", "branch") ?? "-";
|
||||
var author = GetNestedString(mr, "author", "name") ?? "-";
|
||||
var webUrl = GetNestedString(mr, "web_url") ?? "-";
|
||||
|
||||
lines.Add($"#{iid} - {state}\n {title}");
|
||||
lines.Add($" {sourceBranch} -> {targetBranch}\n author: {author}\n URL: {webUrl}");
|
||||
}
|
||||
|
||||
return $"Merge Requests проекта #{projectId} ({mrElement.GetArrayLength()} шт.):\n{string.Join('\n', lines)}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return FormatException("list_merge_requests", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить конкретный MR
|
||||
/// </summary>
|
||||
/// <param name="projectId">ID проекта</param>
|
||||
/// <param name="mrIid">ID Merge Request</param>
|
||||
/// <param name="cancellationToken">Token отмены</param>
|
||||
[McpServerTool, Description("Получить конкретный Merge Request")]
|
||||
public async Task<string> GetMergeRequest(
|
||||
[Description("ID проекта")] int projectId,
|
||||
[Description("ID Merge Request")] int mrIid,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!TryCheckEnabled("GetMergeRequest", out var enabledError)) return enabledError;
|
||||
|
||||
if (projectId <= 0)
|
||||
{
|
||||
return "ID проекта GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (mrIid <= 0)
|
||||
{
|
||||
return "ID Merge Request GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (!TryGetClient(out var client, out var error)) return error;
|
||||
|
||||
try
|
||||
{
|
||||
var request = new RestRequest($"/projects/{projectId}/merge_requests/{mrIid}", RestSharp.Method.Get);
|
||||
request.AddHeader("Accept", "application/json");
|
||||
request.AddHeader("PRIVATE-TOKEN", _token);
|
||||
|
||||
var response = await client.ExecuteAsync(request, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
|
||||
{
|
||||
return FormatResponseError("get_merge_request", response, $"/projects/{projectId}/merge_requests/{mrIid}");
|
||||
}
|
||||
|
||||
using var document = JsonDocument.Parse(response.Content);
|
||||
var root = document.RootElement;
|
||||
|
||||
var iid = GetNestedString(root, "iid") ?? "-";
|
||||
var title = GetNestedString(root, "title") ?? "-";
|
||||
var state = GetState(GetNestedString(root, "state") ?? "");
|
||||
var sourceBranch = GetNestedString(root, "source", "branch") ?? "-";
|
||||
var targetBranch = GetNestedString(root, "target", "branch") ?? "-";
|
||||
var author = GetNestedString(root, "author", "name") ?? "-";
|
||||
var webUrl = GetNestedString(root, "web_url") ?? "-";
|
||||
var mergedAt = GetNestedString(root, "merged_at") ?? "-";
|
||||
var status = GetNestedString(root, "status") ?? "unknown";
|
||||
|
||||
return $"Merge Request #{iid} в проекте #{projectId}:\n{title} [{state}]\n {sourceBranch} -> {targetBranch}\n author: {author}\n URL: {webUrl}\n merged_at: {mergedAt}\n status: {status}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return FormatException("get_merge_request", ex, $"/projects/{projectId}/merge_requests/{mrIid}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создать Merge Request
|
||||
/// </summary>
|
||||
/// <param name="projectId">ID проекта</param>
|
||||
/// <param name="title">Заголовок MR</param>
|
||||
/// <param name="sourceBranch">Имя ветки источника</param>
|
||||
/// <param name="targetBranch">Имя целевой ветки</param>
|
||||
/// <param name="description">Описание (опционально)</param>
|
||||
/// <param name="cancellationToken">Token отмены</param>
|
||||
[McpServerTool, Description("Создать Merge Request")]
|
||||
public async Task<string> CreateMergeRequest(
|
||||
[Description("ID проекта")] int projectId,
|
||||
[Description("Заголовок MR")] string title,
|
||||
[Description("Имя ветки источника")] string sourceBranch,
|
||||
[Description("Имя целевой ветки")] string targetBranch,
|
||||
[Description("Описание MR")] string? description = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!TryCheckEnabled("CreateMergeRequest", out var enabledError)) return enabledError;
|
||||
|
||||
if (projectId <= 0)
|
||||
{
|
||||
return "ID проекта GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
return "Заголовок MR GitLab не может быть пустым.";
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(sourceBranch))
|
||||
{
|
||||
return "Имя ветки источника GitLab не может быть пустым.";
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(targetBranch))
|
||||
{
|
||||
return "Имя целевой ветки GitLab не может быть пустым.";
|
||||
}
|
||||
|
||||
if (!TryGetClient(out var client, out var error)) return error;
|
||||
|
||||
try
|
||||
{
|
||||
var request = new RestRequest($"/projects/{projectId}/merge_requests", RestSharp.Method.Post);
|
||||
request.AddHeader("Accept", "application/json");
|
||||
request.AddHeader("PRIVATE-TOKEN", _token);
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
|
||||
var jsonBody = new
|
||||
{
|
||||
title = title,
|
||||
source_branch = sourceBranch,
|
||||
target_branch = targetBranch,
|
||||
description = description ?? string.Empty
|
||||
}.ToJson();
|
||||
request.AddJsonBody(jsonBody);
|
||||
|
||||
var response = await client.ExecuteAsync(request, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
|
||||
{
|
||||
return FormatResponseError("create_merge_request", response, $"/projects/{projectId}/merge_requests");
|
||||
}
|
||||
|
||||
using var document = JsonDocument.Parse(response.Content);
|
||||
var root = document.RootElement;
|
||||
|
||||
var iid = GetNestedString(root, "iid") ?? "-";
|
||||
var mrTitle = GetNestedString(root, "title") ?? "-";
|
||||
var state = GetState(GetNestedString(root, "state") ?? "");
|
||||
var webUrl = GetNestedString(root, "web_url") ?? "-";
|
||||
|
||||
return $"Merge Request успешно создан в проекте #{projectId}:\nID: #{iid}\n{mrTitle} [{state}]\nURL: {webUrl}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return FormatException("create_merge_request", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Закрыть MR
|
||||
/// </summary>
|
||||
/// <param name="projectId">ID проекта</param>
|
||||
/// <param name="mrIid">ID Merge Request</param>
|
||||
/// <param name="cancellationToken">Token отмены</param>
|
||||
[McpServerTool, Description("Закрыть Merge Request")]
|
||||
public async Task<string> CloseMergeRequest(
|
||||
[Description("ID проекта")] int projectId,
|
||||
[Description("ID Merge Request")] int mrIid,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!TryCheckEnabled("CloseMergeRequest", out var enabledError)) return enabledError;
|
||||
|
||||
if (projectId <= 0)
|
||||
{
|
||||
return "ID проекта GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (mrIid <= 0)
|
||||
{
|
||||
return "ID Merge Request GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (!TryGetClient(out var client, out var error)) return error;
|
||||
|
||||
try
|
||||
{
|
||||
var request = new RestRequest($"/projects/{projectId}/merge_requests/{mrIid}", RestSharp.Method.Put);
|
||||
request.AddHeader("Accept", "application/json");
|
||||
request.AddHeader("PRIVATE-TOKEN", _token);
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
|
||||
var jsonBody = new { state = "closed" }.ToJson();
|
||||
request.AddJsonBody(jsonBody);
|
||||
|
||||
var response = await client.ExecuteAsync(request, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
|
||||
{
|
||||
return FormatResponseError("close_merge_request", response, $"/projects/{projectId}/merge_requests/{mrIid}");
|
||||
}
|
||||
|
||||
using var document = JsonDocument.Parse(response.Content);
|
||||
var root = document.RootElement;
|
||||
|
||||
var mrTitle = GetNestedString(root, "title") ?? "-";
|
||||
|
||||
return $"Merge Request #{mrIid} ({mrTitle}) успешно закрыт в проекте #{projectId}.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return FormatException("close_merge_request", ex, $"/projects/{projectId}/merge_requests/{mrIid}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Открыть MR
|
||||
/// </summary>
|
||||
/// <param name="projectId">ID проекта</param>
|
||||
/// <param name="mrIid">ID Merge Request</param>
|
||||
/// <param name="cancellationToken">Token отмены</param>
|
||||
[McpServerTool, Description("Открыть Merge Request")]
|
||||
public async Task<string> OpenMergeRequest(
|
||||
[Description("ID проекта")] int projectId,
|
||||
[Description("ID Merge Request")] int mrIid,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!TryCheckEnabled("OpenMergeRequest", out var enabledError)) return enabledError;
|
||||
|
||||
if (projectId <= 0)
|
||||
{
|
||||
return "ID проекта GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (mrIid <= 0)
|
||||
{
|
||||
return "ID Merge Request GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (!TryGetClient(out var client, out var error)) return error;
|
||||
|
||||
try
|
||||
{
|
||||
var request = new RestRequest($"/projects/{projectId}/merge_requests/{mrIid}", RestSharp.Method.Put);
|
||||
request.AddHeader("Accept", "application/json");
|
||||
request.AddHeader("PRIVATE-TOKEN", _token);
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
|
||||
var jsonBody = new { state = "opened" }.ToJson();
|
||||
request.AddJsonBody(jsonBody);
|
||||
|
||||
var response = await client.ExecuteAsync(request, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
|
||||
{
|
||||
return FormatResponseError("open_merge_request", response, $"/projects/{projectId}/merge_requests/{mrIid}");
|
||||
}
|
||||
|
||||
using var document = JsonDocument.Parse(response.Content);
|
||||
var root = document.RootElement;
|
||||
|
||||
var mrTitle = GetNestedString(root, "title") ?? "-";
|
||||
|
||||
return $"Merge Request #{mrIid} ({mrTitle}) успешно открыт в проекте #{projectId}.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return FormatException("open_merge_request", ex, $"/projects/{projectId}/merge_requests/{mrIid}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить замечания к MR
|
||||
/// </summary>
|
||||
/// <param name="projectId">ID проекта</param>
|
||||
/// <param name="mrIid">ID Merge Request</param>
|
||||
/// <param name="cancellationToken">Token отмены</param>
|
||||
[McpServerTool, Description("Получить замечания к Merge Request")]
|
||||
public async Task<string> ListMergeRequestNotes(
|
||||
[Description("ID проекта")] int projectId,
|
||||
[Description("ID Merge Request")] int mrIid,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!TryCheckEnabled("ListMergeRequestNotes", out var enabledError)) return enabledError;
|
||||
|
||||
if (projectId <= 0)
|
||||
{
|
||||
return "ID проекта GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (mrIid <= 0)
|
||||
{
|
||||
return "ID Merge Request GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (!TryGetClient(out var client, out var error)) return error;
|
||||
|
||||
try
|
||||
{
|
||||
var request = new RestRequest($"/projects/{projectId}/merge_requests/{mrIid}/notes", RestSharp.Method.Get);
|
||||
request.AddHeader("Accept", "application/json");
|
||||
request.AddHeader("PRIVATE-TOKEN", _token);
|
||||
request.AddQueryParameter("per_page", "30");
|
||||
|
||||
var response = await client.ExecuteAsync(request, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
|
||||
{
|
||||
return FormatResponseError("list_merge_request_notes", response, $"/projects/{projectId}/merge_requests/{mrIid}/notes");
|
||||
}
|
||||
|
||||
using var document = JsonDocument.Parse(response.Content);
|
||||
var root = document.RootElement;
|
||||
|
||||
if (!root.TryGetProperty("notes", out var notesElement) || notesElement.GetArrayLength() == 0)
|
||||
{
|
||||
return $"Замечаний к MR #{mrIid} не найдено.";
|
||||
}
|
||||
|
||||
var lines = new List<string>();
|
||||
foreach (var note in notesElement.EnumerateArray())
|
||||
{
|
||||
var author = GetNestedString(note, "author", "name") ?? "-";
|
||||
var createdAt = GetNestedString(note, "created_at") ?? "-";
|
||||
var subject = GetNestedString(note, "subject") ?? "-";
|
||||
var body = GetNestedString(note, "body") ?? "-";
|
||||
|
||||
lines.Add($"Author: {author}, Time: {createdAt}\n Subject: {subject}\n Body: {body}");
|
||||
}
|
||||
|
||||
return $"Замечания к MR #{mrIid} ({notesElement.GetArrayLength()} шт.):\n{string.Join('\n', lines)}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return FormatException("list_merge_request_notes", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Добавить замечание к MR
|
||||
/// </summary>
|
||||
/// <param name="projectId">ID проекта</param>
|
||||
/// <param name="mrIid">ID Merge Request</param>
|
||||
/// <param name="body">Текст замечания</param>
|
||||
/// <param name="subject">Заголовок замечания (опционально)</param>
|
||||
/// <param name="cancellationToken">Token отмены</param>
|
||||
[McpServerTool, Description("Добавить замечание к Merge Request")]
|
||||
public async Task<string> CreateMergeRequestNote(
|
||||
[Description("ID проекта")] int projectId,
|
||||
[Description("ID Merge Request")] int mrIid,
|
||||
[Description("Текст замечания")] string body,
|
||||
[Description("Заголовок замечания")] string? subject = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!TryCheckEnabled("CreateMergeRequestNote", out var enabledError)) return enabledError;
|
||||
|
||||
if (projectId <= 0)
|
||||
{
|
||||
return "ID проекта GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (mrIid <= 0)
|
||||
{
|
||||
return "ID Merge Request GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(body))
|
||||
{
|
||||
return "Текст замечания GitLab не может быть пустым.";
|
||||
}
|
||||
|
||||
if (!TryGetClient(out var client, out var error)) return error;
|
||||
|
||||
try
|
||||
{
|
||||
var request = new RestRequest($"/projects/{projectId}/merge_requests/{mrIid}/notes", RestSharp.Method.Post);
|
||||
request.AddHeader("Accept", "application/json");
|
||||
request.AddHeader("PRIVATE-TOKEN", _token);
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
|
||||
var jsonBody = new
|
||||
{
|
||||
body = body,
|
||||
subject = subject ?? string.Empty
|
||||
}.ToJson();
|
||||
request.AddJsonBody(jsonBody);
|
||||
|
||||
var response = await client.ExecuteAsync(request, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
|
||||
{
|
||||
return FormatResponseError("create_merge_request_note", response, $"/projects/{projectId}/merge_requests/{mrIid}/notes");
|
||||
}
|
||||
|
||||
using var document = JsonDocument.Parse(response.Content);
|
||||
var root = document.RootElement;
|
||||
|
||||
var noteId = GetNestedString(root, "id") ?? "-";
|
||||
var noteSubject = GetNestedString(root, "subject") ?? "-";
|
||||
var noteBody = GetNestedString(root, "body") ?? "-";
|
||||
|
||||
return $"Замечание успешно добавлено к MR #{mrIid}:\nID: #{noteId}\n{noteSubject}\n{noteBody}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return FormatException("create_merge_request_note", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удалить замечание из MR
|
||||
/// </summary>
|
||||
/// <param name="projectId">ID проекта</param>
|
||||
/// <param name="mrIid">ID Merge Request</param>
|
||||
/// <param name="noteId">ID замечания</param>
|
||||
/// <param name="cancellationToken">Token отмены</param>
|
||||
[McpServerTool, Description("Удалить замечание из Merge Request")]
|
||||
public async Task<string> DeleteMergeRequestNote(
|
||||
[Description("ID проекта")] int projectId,
|
||||
[Description("ID Merge Request")] int mrIid,
|
||||
[Description("ID замечания")] int noteId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!TryCheckEnabled("DeleteMergeRequestNote", out var enabledError)) return enabledError;
|
||||
|
||||
if (projectId <= 0)
|
||||
{
|
||||
return "ID проекта GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (mrIid <= 0)
|
||||
{
|
||||
return "ID Merge Request GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (noteId <= 0)
|
||||
{
|
||||
return "ID замечания GitLab некорректно задан.";
|
||||
}
|
||||
|
||||
if (!TryGetClient(out var client, out var error)) return error;
|
||||
|
||||
try
|
||||
{
|
||||
var request = new RestRequest($"/projects/{projectId}/merge_requests/{mrIid}/notes/{noteId}", RestSharp.Method.Delete);
|
||||
request.AddHeader("Accept", "application/json");
|
||||
request.AddHeader("PRIVATE-TOKEN", _token);
|
||||
|
||||
var response = await client.ExecuteAsync(request, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
|
||||
{
|
||||
return FormatResponseError("delete_merge_request_note", response, $"/projects/{projectId}/merge_requests/{mrIid}/notes/{noteId}");
|
||||
}
|
||||
|
||||
using var document = JsonDocument.Parse(response.Content);
|
||||
var root = document.RootElement;
|
||||
|
||||
var noteSubject = GetNestedString(root, "subject") ?? "-";
|
||||
|
||||
return $"Замечание #{noteId} ({noteSubject}) успешно удалено из MR #{mrIid}.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return FormatException("delete_merge_request_note", ex, $"/projects/{projectId}/merge_requests/{mrIid}/notes/{noteId}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user