Skip to content

Commit

Permalink
bugfix(DtmSample): fix MsgMsSqlQueryPrepared return http status 200 w…
Browse files Browse the repository at this point in the history
…ith unrecognized error body. (#86)

* bugfix(DtmSample): fix MsgMsSqlQueryPrepared return http status 200 with unrecognized error body.

- http status code 200 with unrecognized body will be as normal!
- Add Result2HttpJson method for consistent error handling and HTTP response formatting
- Update MsgMssqlQueryPrepared action to use the new method

* test(DtmSample): add test case for MSG MSSQL DB connection error, expected status should be prepared

* refactor(QueryPrepared): Add OrString、String2DtmError、Result2HttpJson methods to Utils.cs

- Add OrString、String2DtmError、Result2HttpJson methods to Utils.cs
- Refactor sample controller Msg QueryPrepared, by invoking new methods in Utils.cs
  • Loading branch information
wooln authored Dec 18, 2024
1 parent 40711b8 commit 9926030
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 19 deletions.
5 changes: 5 additions & 0 deletions samples/DtmSample/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@
{
public class AppSettings
{
public string DtmUrl { get; set; }

public string BusiUrl { get; set; }

public string SqlBarrierConn { get; set; }


public string SqlBarrierErrorConn { get; set; }

public string MongoBarrierConn { get; set; }
}
}
84 changes: 66 additions & 18 deletions samples/DtmSample/Controllers/MsgTestController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Dtmcli;
using System;
using Dtmcli;
using DtmMongoBarrier;
using DtmSample.Dtos;
using Microsoft.AspNetCore.Mvc;
Expand Down Expand Up @@ -36,6 +37,9 @@ public MsgTestController(ILogger<MsgTestController> logger, IOptions<AppSettings
private MySqlConnection GetMysqlConn() => new(_settings.SqlBarrierConn);

private SqlConnection GetMssqlConn() => new(_settings.SqlBarrierConn);

private SqlConnection GetBadMssqlConn() => new(_settings.SqlBarrierErrorConn);


private MySqlConnection GetErrConn() => new("");

Expand Down Expand Up @@ -115,6 +119,40 @@ await msg.DoAndSubmitDB(_settings.BusiUrl + "/msg-mssqlqueryprepared", conn, asy
return Ok(TransResponse.BuildSucceedResponse());
}


/// <summary>
/// MSG DoAndSubmitDB (mssql). db connection error, DTM server Status should be prepared.
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpPost("msg-db-mssql-db-connection-error")]
public async Task<IActionResult> MsgDbMsSql_DbConnectionError(CancellationToken cancellationToken)
{
var gid = await _dtmClient.GenGid(cancellationToken);

var msg = _transFactory.NewMsg(gid)
.Add(_settings.BusiUrl + "/TransOut", new TransRequest("1", -30))
.Add(_settings.BusiUrl + "/TransIn", new TransRequest("2", 30));

try
{
using (SqlConnection conn = GetBadMssqlConn())
{
await msg.DoAndSubmitDB(_settings.BusiUrl + "/msg-mssqlqueryprepared", conn, async tx => { await Task.CompletedTask; });
}
}
catch (SqlException)
{
Thread.Sleep(5 * 1000);
_logger.LogInformation("{}/admin/global-transactions/detail/{}, status should be prepared", _settings.DtmUrl, gid);
throw;
}

_logger.LogInformation("result gid is {0}", gid);
return Ok(TransResponse.BuildSucceedResponse());
}


/// <summary>
/// MSG DoAndSubmit (mongo)
/// </summary>
Expand Down Expand Up @@ -184,7 +222,22 @@ public async Task<IActionResult> MsgMySqlQueryPrepared(CancellationToken cancell
}
}

/// <summary>
/// MSG QueryPrepared(mongo)
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("msg-mongoqueryprepared")]
public async Task<IActionResult> MsgMongoQueryPrepared(CancellationToken cancellationToken)
{
var bb = _factory.CreateBranchBarrier(Request.Query);
_logger.LogInformation("bb {0}", bb);

MongoDB.Driver.IMongoClient cli = new MongoDB.Driver.MongoClient(_settings.MongoBarrierConn);
var res = await bb.MongoQueryPrepared(cli);
return Ok(new { dtm_result = res });
}

/// <summary>
/// MSG QueryPrepared(mssql)
///
Expand All @@ -211,28 +264,23 @@ public async Task<IActionResult> MsgMsSqlQueryPrepared(CancellationToken cancell
{
var bb = _factory.CreateBranchBarrier(Request.Query);
_logger.LogInformation("bb {0}", bb);
using (SqlConnection conn = GetMssqlConn())
{
var res = await bb.QueryPrepared(conn);

return Ok(new { dtm_result = res });
string ret;
await using (SqlConnection conn = GetMssqlConn())
{
ret = await bb.QueryPrepared(conn);
}

ret = Dtmcli.DtmImp.Utils.OrString(ret, DtmCommon.Constant.ResultSuccess);
Exception error = Dtmcli.DtmImp.Utils.String2DtmError(ret);

return WrapHandler(error);
}

/// <summary>
/// MSG QueryPrepared(mongo)
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("msg-mongoqueryprepared")]
public async Task<IActionResult> MsgMongoQueryPrepared(CancellationToken cancellationToken)
private IActionResult WrapHandler(Exception error)
{
var bb = _factory.CreateBranchBarrier(Request.Query);
_logger.LogInformation("bb {0}", bb);

MongoDB.Driver.IMongoClient cli = new MongoDB.Driver.MongoClient(_settings.MongoBarrierConn);
var res = await bb.MongoQueryPrepared(cli);
return Ok(new { dtm_result = res });
(int status, object res) = Dtmcli.DtmImp.Utils.Result2HttpJson(error);
return StatusCode(status, res);
}

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions samples/DtmSample/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"SqlDbType": "sqlserver",
"BarrierSqlTableName": "dbo.barrier",
"SqlBarrierConn": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=dtm_barrier;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
"SqlBarrierErrorConn": "Data Source=.;Initial Catalog=dtm_barrier;User ID=sa;Password=my_error_password;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
"MongoBarrierConn": "mongodb://localhost:27017"
}
}
56 changes: 56 additions & 0 deletions src/Dtmcli/DtmImp/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,61 @@ public static void CheckStatus(HttpStatusCode status, string dtmResult)
throw new DtmException(string.Format(CheckStatusMsgFormat, status.ToString(), dtmResult));
}
}

/// <summary>
/// OrString return the first not null or not empty string
/// </summary>
/// <param name="ss"></param>
/// <returns></returns>
public static string OrString(params string[] ss)
{
foreach (var s in ss)
{
if (!string.IsNullOrEmpty(s))
return s;
}

return "";
}

/// <summary>
/// translate string to dtm error
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static Exception String2DtmError(string str)
{
if (str == DtmCommon.Constant.ResultSuccess || str == string.Empty)
return null;
if (str == string.Empty)
return null;
if (str == DtmCommon.Constant.ResultFailure)
return new DtmCommon.DtmFailureException();
if (str == DtmCommon.Constant.ResultOngoing)
return new DtmCommon.DtmOngingException();
return new Exception(str);
}

/// <summary>
/// translate object to http response
/// 409 => ErrFailure; Code 425 => ErrOngoing; Code 500 => InternalServerError
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
public static (int httpStatusCode, object res) Result2HttpJson(object result)
{
if (result is not Exception err)
{
return ((int)(HttpStatusCode.OK), result);
}

var res = new { error = err.Message };
if (err is DtmFailureException)
return ((int)HttpStatusCode.Conflict, res);
if (err is DtmOngingException)
return (425 /*HttpStatusCode.TooEarly*/, res);

return ((int)HttpStatusCode.InternalServerError, res);
}
}
}
45 changes: 44 additions & 1 deletion tests/Dtmcli.Tests/UtilsTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using Xunit;
using System;
using Xunit;
using System.Net;
using System.Net.Http;
using System.Text.Json.Serialization;
using DtmCommon;
using Newtonsoft.Json;

namespace Dtmcli.Tests
{
Expand Down Expand Up @@ -62,5 +66,44 @@ public void CheckStatus_Should_Throw_Failure_Exception(HttpStatusCode code, stri
{
Assert.Throws<DtmCommon.DtmException>(() => DtmImp.Utils.CheckStatus(code, msg));
}

[Fact]
public void OrString()
{
Assert.Equal("", DtmImp.Utils.OrString());
Assert.Equal("A", DtmImp.Utils.OrString("", "A"));
Assert.Equal("A", DtmImp.Utils.OrString("", "A", "B"));
Assert.Equal("A", DtmImp.Utils.OrString("A", "B"));
}

[Fact]
public void String2DtmError()
{
Assert.IsType<Exception>(DtmImp.Utils.String2DtmError(null));
Assert.Null(DtmImp.Utils.String2DtmError(string.Empty));
Assert.Null(DtmImp.Utils.String2DtmError("SUCCESS"));
Assert.IsType<DtmFailureException>(DtmImp.Utils.String2DtmError("FAILURE"));
Assert.IsType<DtmOngingException>(DtmImp.Utils.String2DtmError("ONGOING"));
Assert.IsType<Exception>(DtmImp.Utils.String2DtmError("Object ..."));
}

[Fact]
public void Result2HttpJson()
{
Assert.Equal(200, DtmImp.Utils.Result2HttpJson(null).httpStatusCode);
Assert.Equal(409, DtmImp.Utils.Result2HttpJson(new DtmFailureException()).httpStatusCode);
Assert.Equal(425, DtmImp.Utils.Result2HttpJson(new DtmOngingException()).httpStatusCode);
Assert.Equal(500, DtmImp.Utils.Result2HttpJson(new DtmDuplicatedException()).httpStatusCode);

Assert.Equal(500, DtmImp.Utils.Result2HttpJson(new Exception("message context A")).httpStatusCode);
Assert.Contains("message context A", JsonConvert.SerializeObject(DtmImp.Utils.Result2HttpJson(new Exception("message context A")).res));

Assert.Equal(200, DtmImp.Utils.Result2HttpJson("normal text").httpStatusCode);
Assert.Equal("normal text", DtmImp.Utils.Result2HttpJson("normal text").res);

var obj = new { A = "hello", B = "world" };
Assert.Equal(200, DtmImp.Utils.Result2HttpJson(obj).httpStatusCode);
Assert.Equal(obj, DtmImp.Utils.Result2HttpJson(obj).res);
}
}
}

0 comments on commit 9926030

Please sign in to comment.