From 6ca3b79f25ed0430ef73838510f4426215a10cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E4=BA=91=E9=87=91YunjinXu?= Date: Wed, 11 Dec 2024 17:32:16 +0800 Subject: [PATCH] feat(DtmClient): add QueryStatus method for query single global transaction status --- .../Controllers/MsgTestController.cs | 16 ++++- src/Dtmcli/DtmClient.cs | 22 +++++++ src/Dtmcli/IDtmClient.cs | 2 + src/Dtmcli/TransGlobal.cs | 13 +++++ tests/Dtmcli.Tests/DtmClientTests.cs | 58 +++++++++++++++++++ 5 files changed, 109 insertions(+), 2 deletions(-) diff --git a/samples/DtmSample/Controllers/MsgTestController.cs b/samples/DtmSample/Controllers/MsgTestController.cs index e0816a1..5f01d4e 100644 --- a/samples/DtmSample/Controllers/MsgTestController.cs +++ b/samples/DtmSample/Controllers/MsgTestController.cs @@ -276,9 +276,9 @@ public async Task MsgWithTopic(CancellationToken cancellationToke return Ok(TransResponse.BuildSucceedResponse()); } - + /// - /// MSG with exist topic + /// query /// /// /// @@ -288,5 +288,17 @@ public async Task Query(string gid, CancellationToken cancellatio TransGlobal trans = await _dtmClient.Query(gid, cancellationToken); return Ok(trans); } + + /// + /// query status + /// + /// + /// + [HttpGet("query-status")] + public async Task QueryStatus(string gid, CancellationToken cancellationToken) + { + string status = await _dtmClient.QueryStatus(gid, cancellationToken); + return Ok(status); + } } } diff --git a/src/Dtmcli/DtmClient.cs b/src/Dtmcli/DtmClient.cs index 1806bb6..7a811e3 100644 --- a/src/Dtmcli/DtmClient.cs +++ b/src/Dtmcli/DtmClient.cs @@ -152,6 +152,28 @@ public async Task Query(string gid, CancellationToken cancellationT return JsonSerializer.Deserialize(dtmContent, _jsonOptions); } + /// + /// Query single global transaction status + /// + /// + /// + /// + /// + public async Task QueryStatus(string gid, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(gid)) throw new ArgumentNullException(nameof(gid)); + + var url = string.Concat(_dtmOptions.DtmUrl.TrimEnd(Slash), Constant.Request.URL_Query, $"?gid={gid}"); + var client = _httpClientFactory.CreateClient(Constant.DtmClientHttpName); + var response = await client.GetAsync(url, cancellationToken).ConfigureAwait(false); + var dtmContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + DtmImp.Utils.CheckStatus(response.StatusCode, dtmContent); + var graph = JsonSerializer.Deserialize(dtmContent, _jsonOptions); + return graph.Transaction == null + ? string.Empty + : graph.Transaction.Status; + } + public class DtmGid { [JsonPropertyName("gid")] diff --git a/src/Dtmcli/IDtmClient.cs b/src/Dtmcli/IDtmClient.cs index 6d1c29a..9f02d1a 100644 --- a/src/Dtmcli/IDtmClient.cs +++ b/src/Dtmcli/IDtmClient.cs @@ -25,5 +25,7 @@ public interface IDtmClient #endif Task Query(string gid, CancellationToken cancellationToken); + + Task QueryStatus(string gid, CancellationToken cancellationToken); } } diff --git a/src/Dtmcli/TransGlobal.cs b/src/Dtmcli/TransGlobal.cs index 9eb6de1..2b68782 100644 --- a/src/Dtmcli/TransGlobal.cs +++ b/src/Dtmcli/TransGlobal.cs @@ -4,6 +4,19 @@ namespace Dtmcli; +/// +/// query status only +/// +internal class TransGlobalForStatus +{ + [JsonPropertyName("transaction")] public DtmTransactionForStatus Transaction { get; set; } + + public class DtmTransactionForStatus + { + [JsonPropertyName("status")] public string Status { get; set; } + } +} + // convert from json(a prepared TCC global trans sample) to c# code public class TransGlobal { diff --git a/tests/Dtmcli.Tests/DtmClientTests.cs b/tests/Dtmcli.Tests/DtmClientTests.cs index d81d6b6..59279e0 100644 --- a/tests/Dtmcli.Tests/DtmClientTests.cs +++ b/tests/Dtmcli.Tests/DtmClientTests.cs @@ -198,6 +198,64 @@ public async Task Query_Should_Throw_Exception() await client.Query(gid: "my-gid", new CancellationToken()); }); } + + + + [Fact] + public async Task QueryStatus_Should_Succeed() + { + var factory = new Mock(); + var options = Microsoft.Extensions.Options.Options.Create(new DtmCommon.DtmOptions { DtmUrl = "http://localhost:8080" }); + /* + { + "branches": [], + "transaction": { + "id": 7, + "gid": "mV9RGqZCV2mdn9YA6T2TPC", + "trans_type": "msg", + "status": "prepared", + "protocol": "http" + } + } + */ + var mockHttpMessageHandler = new ClientMockHttpMessageHandler(HttpStatusCode.OK, "{\n \"branches\": [],\n \"transaction\": {\n \"id\": 7,\n \"gid\": \"mV9RGqZCV2mdn9YA6T2TPC\",\n \"trans_type\": \"msg\",\n \"status\": \"prepared\",\n \"protocol\": \"http\"\n }\n} "); + factory.Setup(x => x.CreateClient(It.IsAny())).Returns(new HttpClient(mockHttpMessageHandler)); + var client = new DtmClient(factory.Object, options); + string status = await client.QueryStatus(gid: "my-gid", new CancellationToken()); + Assert.Equal("prepared", status); + } + + [Fact] + public async Task QueryStatus_Not_Exist_Gid() + { + var factory = new Mock(); + var options = Microsoft.Extensions.Options.Options.Create(new DtmCommon.DtmOptions { DtmUrl = "http://localhost:8080" }); + /* + { + "branches": [], + "transaction": null + } + */ + var mockHttpMessageHandler = new ClientMockHttpMessageHandler(HttpStatusCode.OK, "{\n \"branches\": [],\n \"transaction\": null\n}\n"); + factory.Setup(x => x.CreateClient(It.IsAny())).Returns(new HttpClient(mockHttpMessageHandler)); + var client = new DtmClient(factory.Object, options); + string status = await client.QueryStatus(gid: "my-gid", new CancellationToken()); + Assert.Empty(status); + } + + [Fact] + public async Task QueryStatus_Should_Throw_Exception() + { + var factory = new Mock(); + var options = Microsoft.Extensions.Options.Options.Create(new DtmCommon.DtmOptions { DtmUrl = "http://localhost:8080" }); + var mockHttpMessageHandler = new ClientMockHttpMessageHandler(HttpStatusCode.InternalServerError, ""); + factory.Setup(x => x.CreateClient(It.IsAny())).Returns(new HttpClient(mockHttpMessageHandler)); + var client = new DtmClient(factory.Object, options); + await Assert.ThrowsAsync(async () => + { + await client.QueryStatus(gid: "my-gid", new CancellationToken()); + }); + } } internal class ClientMockHttpMessageHandler : DelegatingHandler