607 lines
24 KiB
C#
607 lines
24 KiB
C#
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}");
|
|
}
|
|
}
|
|
} |